Commit 1830067081d4de1f4355539fb498a46d4aa97aaf

Authored by wuxw
1 parent d4a6b78f

工作单功能处理中

src/api/oa/addWorkApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +// 保存工作单
  5 +export function saveWorkPool(data) {
  6 + return new Promise((resolve, reject) => {
  7 + request({
  8 + url: '/work.saveWorkPool',
  9 + method: 'post',
  10 + data: {
  11 + ...data,
  12 + communityId: getCommunityId()
  13 + }
  14 + }).then(response => {
  15 + resolve(response.data)
  16 + }).catch(error => {
  17 + reject(error)
  18 + })
  19 + })
  20 +}
  21 +
  22 +// 获取工作单类型列表
  23 +export function listWorkType(params) {
  24 + return new Promise((resolve, reject) => {
  25 + request({
  26 + url: '/workType.listWorkType',
  27 + method: 'get',
  28 + params: {
  29 + ...params,
  30 + communityId: getCommunityId()
  31 + }
  32 + }).then(response => {
  33 + resolve(response.data)
  34 + }).catch(error => {
  35 + reject(error)
  36 + })
  37 + })
  38 +}
  39 +
  40 +// 获取组织树
  41 +export function listOrgTree() {
  42 + return new Promise((resolve, reject) => {
  43 + request({
  44 + url: '/org.listOrgTree',
  45 + method: 'get',
  46 + params: {
  47 + communityId: getCommunityId()
  48 + }
  49 + }).then(response => {
  50 + resolve(response.data)
  51 + }).catch(error => {
  52 + reject(error)
  53 + })
  54 + })
  55 +}
  56 +
  57 +// 查询员工信息
  58 +export function queryStaffInfos(params) {
  59 + return new Promise((resolve, reject) => {
  60 + request({
  61 + url: '/query.staff.infos',
  62 + method: 'get',
  63 + params: {
  64 + ...params,
  65 + communityId: getCommunityId()
  66 + }
  67 + }).then(response => {
  68 + resolve(response.data)
  69 + }).catch(error => {
  70 + reject(error)
  71 + })
  72 + })
  73 +}
  74 +
  75 +// 上传文件
  76 +export function uploadFile(data) {
  77 + return new Promise((resolve, reject) => {
  78 + request({
  79 + url: '/upload',
  80 + method: 'post',
  81 + data,
  82 + headers: {
  83 + 'Content-Type': 'multipart/form-data'
  84 + }
  85 + }).then(response => {
  86 + resolve(response.data)
  87 + }).catch(error => {
  88 + reject(error)
  89 + })
  90 + })
  91 +}
0 \ No newline at end of file 92 \ No newline at end of file
src/api/oa/editWorkApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 获取工作单类型列表
  4 +export function listWorkType(params) {
  5 + return new Promise((resolve, reject) => {
  6 + request({
  7 + url: '/workType.listWorkType',
  8 + method: 'get',
  9 + params
  10 + }).then(response => {
  11 + const res = response.data
  12 + resolve(res)
  13 + }).catch(error => {
  14 + reject(error)
  15 + })
  16 + })
  17 +}
  18 +
  19 +// 查询工作单详情
  20 +export function queryStartWork(params) {
  21 + return new Promise((resolve, reject) => {
  22 + request({
  23 + url: '/work.queryStartWork',
  24 + method: 'get',
  25 + params
  26 + }).then(response => {
  27 + const res = response.data
  28 + resolve(res)
  29 + }).catch(error => {
  30 + reject(error)
  31 + })
  32 + })
  33 +}
  34 +
  35 +// 获取工作单处理人列表
  36 +export function listWorkTask(params) {
  37 + return new Promise((resolve, reject) => {
  38 + request({
  39 + url: '/work.listWorkTask',
  40 + method: 'get',
  41 + params
  42 + }).then(response => {
  43 + const res = response.data
  44 + resolve(res)
  45 + }).catch(error => {
  46 + reject(error)
  47 + })
  48 + })
  49 +}
  50 +
  51 +// 获取工作单抄送人列表
  52 +export function listWorkCopy(params) {
  53 + return new Promise((resolve, reject) => {
  54 + request({
  55 + url: '/work.listWorkCopy',
  56 + method: 'get',
  57 + params
  58 + }).then(response => {
  59 + const res = response.data
  60 + resolve(res)
  61 + }).catch(error => {
  62 + reject(error)
  63 + })
  64 + })
  65 +}
  66 +
  67 +// 获取工作单周期信息
  68 +export function listWorkCycle(params) {
  69 + return new Promise((resolve, reject) => {
  70 + request({
  71 + url: '/workCycle.listWorkCycle',
  72 + method: 'get',
  73 + params
  74 + }).then(response => {
  75 + const res = response.data
  76 + resolve(res)
  77 + }).catch(error => {
  78 + reject(error)
  79 + })
  80 + })
  81 +}
  82 +
  83 +// 更新工作单
  84 +export function updateWorkPool(data) {
  85 + return new Promise((resolve, reject) => {
  86 + request({
  87 + url: '/work.updateWorkPool',
  88 + method: 'post',
  89 + data
  90 + }).then(response => {
  91 + const res = response.data
  92 + resolve(res)
  93 + }).catch(error => {
  94 + reject(error)
  95 + })
  96 + })
  97 +}
  98 +
  99 +// 查询员工信息
  100 +export function queryStaffInfos(params) {
  101 + return new Promise((resolve, reject) => {
  102 + request({
  103 + url: '/query.staff.infos',
  104 + method: 'get',
  105 + params
  106 + }).then(response => {
  107 + const res = response.data
  108 + resolve(res)
  109 + }).catch(error => {
  110 + reject(error)
  111 + })
  112 + })
  113 +}
  114 +
  115 +// 获取组织树
  116 +export function listOrgTree(params) {
  117 + return new Promise((resolve, reject) => {
  118 + request({
  119 + url: '/org.listOrgTree',
  120 + method: 'get',
  121 + params
  122 + }).then(response => {
  123 + const res = response.data
  124 + resolve(res)
  125 + }).catch(error => {
  126 + reject(error)
  127 + })
  128 + })
  129 +}
  130 +
  131 +// 上传文件
  132 +export function uploadFile(data, config) {
  133 + return new Promise((resolve, reject) => {
  134 + request({
  135 + url: '/upload',
  136 + method: 'post',
  137 + data,
  138 + onUploadProgress: config.onUploadProgress,
  139 + headers: {
  140 + 'Content-Type': 'multipart/form-data'
  141 + }
  142 + }).then(response => {
  143 + const res = response.data
  144 + resolve(res)
  145 + }).catch(error => {
  146 + reject(error)
  147 + })
  148 + })
  149 +}
  150 +
  151 +// 上传图片
  152 +export function uploadImage(data) {
  153 + return new Promise((resolve, reject) => {
  154 + request({
  155 + url: '/uploadImage',
  156 + method: 'post',
  157 + data,
  158 + headers: {
  159 + 'Content-Type': 'multipart/form-data'
  160 + }
  161 + }).then(response => {
  162 + const res = response.data
  163 + resolve(res)
  164 + }).catch(error => {
  165 + reject(error)
  166 + })
  167 + })
  168 +}
0 \ No newline at end of file 169 \ No newline at end of file
src/api/oa/startWorkApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +// 查询发起工作单列表
  5 +export function queryStartWork(params) {
  6 + return new Promise((resolve, reject) => {
  7 + request({
  8 + url: '/work.queryStartWork',
  9 + method: 'get',
  10 + params: {
  11 + ...params,
  12 + communityId: getCommunityId()
  13 + }
  14 + }).then(response => {
  15 + const res = response.data
  16 + resolve({
  17 + data: res.data,
  18 + total: res.total
  19 + })
  20 + }).catch(error => {
  21 + reject(error)
  22 + })
  23 + })
  24 +}
  25 +
  26 +// 删除工作单
  27 +export function deleteWorkPool(data) {
  28 + return new Promise((resolve, reject) => {
  29 + request({
  30 + url: '/work.deleteWorkPool',
  31 + method: 'post',
  32 + data: {
  33 + ...data,
  34 + communityId: getCommunityId()
  35 + }
  36 + }).then(response => {
  37 + const res = response.data
  38 + if (res.code === 0) {
  39 + resolve(res)
  40 + } else {
  41 + reject(new Error(res.msg))
  42 + }
  43 + }).catch(error => {
  44 + reject(error)
  45 + })
  46 + })
  47 +}
  48 +
  49 +// 启动/停止工作单
  50 +export function updateWorkState(data) {
  51 + return new Promise((resolve, reject) => {
  52 + request({
  53 + url: '/work.startOrStopWorkPool',
  54 + method: 'post',
  55 + data: {
  56 + ...data,
  57 + communityId: getCommunityId()
  58 + }
  59 + }).then(response => {
  60 + const res = response.data
  61 + if (res.code === 0) {
  62 + resolve(res)
  63 + } else {
  64 + reject(new Error(res.msg))
  65 + }
  66 + }).catch(error => {
  67 + reject(error)
  68 + })
  69 + })
  70 +}
0 \ No newline at end of file 71 \ No newline at end of file
src/api/oa/workTypeApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 获取工作单类型列表
  4 +export function listWorkType(params) {
  5 + return new Promise((resolve, reject) => {
  6 + request({
  7 + url: '/workType.listWorkType',
  8 + method: 'get',
  9 + params
  10 + }).then(response => {
  11 + const res = response.data
  12 + resolve(res)
  13 + }).catch(error => {
  14 + reject(error)
  15 + })
  16 + })
  17 +}
  18 +
  19 +// 添加工作单类型
  20 +export function saveWorkType(data) {
  21 + return new Promise((resolve, reject) => {
  22 + request({
  23 + url: '/workType.saveWorkType',
  24 + method: 'post',
  25 + data
  26 + }).then(response => {
  27 + const res = response.data
  28 + resolve(res)
  29 + }).catch(error => {
  30 + reject(error)
  31 + })
  32 + })
  33 +}
  34 +
  35 +// 更新工作单类型
  36 +export function updateWorkType(data) {
  37 + return new Promise((resolve, reject) => {
  38 + request({
  39 + url: '/workType.updateWorkType',
  40 + method: 'post',
  41 + data
  42 + }).then(response => {
  43 + const res = response.data
  44 + resolve(res)
  45 + }).catch(error => {
  46 + reject(error)
  47 + })
  48 + })
  49 +}
  50 +
  51 +// 删除工作单类型
  52 +export function deleteWorkType(data) {
  53 + return new Promise((resolve, reject) => {
  54 + request({
  55 + url: '/workType.deleteWorkType',
  56 + method: 'post',
  57 + data
  58 + }).then(response => {
  59 + const res = response.data
  60 + resolve(res)
  61 + }).catch(error => {
  62 + reject(error)
  63 + })
  64 + })
  65 +}
0 \ No newline at end of file 66 \ No newline at end of file
src/api/system/paymentPoolApi.js
@@ -153,7 +153,7 @@ export function listFeeConfigs(params) { @@ -153,7 +153,7 @@ export function listFeeConfigs(params) {
153 export function uploadFile(data, config) { 153 export function uploadFile(data, config) {
154 return new Promise((resolve, reject) => { 154 return new Promise((resolve, reject) => {
155 request({ 155 request({
156 - url: '/uploadVedio/upload', 156 + url: '/callComponent/upload/uploadVedio/upload',
157 method: 'post', 157 method: 'post',
158 data, 158 data,
159 headers: { 159 headers: {
src/components/oa/addWorkType.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('workType.add.title')"
  4 + :visible.sync="visible"
  5 + width="50%"
  6 + @close="handleClose"
  7 + >
  8 + <el-form ref="form" :model="formData" :rules="rules" label-width="120px">
  9 + <el-form-item :label="$t('workType.form.typeName')" prop="typeName">
  10 + <el-input
  11 + v-model.trim="formData.typeName"
  12 + :placeholder="$t('workType.placeholder.typeName')"
  13 + />
  14 + </el-form-item>
  15 +
  16 + <el-form-item :label="$t('workType.form.deduction')" prop="deduction">
  17 + <el-select
  18 + v-model="formData.deduction"
  19 + :placeholder="$t('workType.placeholder.deduction')"
  20 + style="width:100%"
  21 + >
  22 + <el-option
  23 + v-for="item in deductionOptions"
  24 + :key="item.value"
  25 + :label="item.label"
  26 + :value="item.value"
  27 + />
  28 + </el-select>
  29 + <div class="el-form-item__tip" v-if="formData.deduction === 'Y'">
  30 + {{ $t('workType.tip.deduction') }}
  31 + </div>
  32 + </el-form-item>
  33 +
  34 + <el-form-item :label="$t('workType.form.smsWay')" prop="smsWay">
  35 + <el-select
  36 + v-model="formData.smsWay"
  37 + :placeholder="$t('workType.placeholder.smsWay')"
  38 + style="width:100%"
  39 + >
  40 + <el-option
  41 + v-for="item in smsWayOptions"
  42 + :key="item.value"
  43 + :label="item.label"
  44 + :value="item.value"
  45 + />
  46 + </el-select>
  47 + <div class="el-form-item__tip" v-if="formData.smsWay === 'WECHAT'">
  48 + {{ $t('workType.tip.wechat') }}
  49 + </div>
  50 + <div class="el-form-item__tip" v-else-if="formData.smsWay === 'ALI_SMS'">
  51 + {{ $t('workType.tip.aliSms') }}
  52 + </div>
  53 + </el-form-item>
  54 +
  55 + <el-form-item :label="$t('workType.form.remark')">
  56 + <el-input
  57 + v-model.trim="formData.remark"
  58 + type="textarea"
  59 + :rows="3"
  60 + :placeholder="$t('workType.placeholder.remark')"
  61 + maxlength="500"
  62 + show-word-limit
  63 + />
  64 + </el-form-item>
  65 + </el-form>
  66 +
  67 + <span slot="footer" class="dialog-footer">
  68 + <el-button @click="visible = false">{{ $t('common.cancel') }}</el-button>
  69 + <el-button type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</el-button>
  70 + </span>
  71 + </el-dialog>
  72 +</template>
  73 +
  74 +<script>
  75 +import { saveWorkType } from '@/api/oa/workTypeApi'
  76 +import { getCommunityId } from '@/api/community/communityApi'
  77 +
  78 +export default {
  79 + name: 'AddWorkType',
  80 + data() {
  81 + return {
  82 + visible: false,
  83 + formData: {
  84 + typeName: '',
  85 + deduction: '',
  86 + smsWay: '',
  87 + remark: ''
  88 + },
  89 + deductionOptions: [
  90 + { value: 'Y', label: this.$t('workType.yes') },
  91 + { value: 'N', label: this.$t('workType.no') }
  92 + ],
  93 + smsWayOptions: [
  94 + { value: 'WECHAT', label: this.$t('workType.wechat') },
  95 + { value: 'ALI_SMS', label: this.$t('workType.aliSms') },
  96 + { value: 'WORK_LICENSE', label: this.$t('workType.workLicense') }
  97 + ],
  98 + rules: {
  99 + typeName: [
  100 + { required: true, message: this.$t('workType.validate.typeName'), trigger: 'blur' },
  101 + { max: 200, message: this.$t('workType.validate.typeNameMax'), trigger: 'blur' }
  102 + ],
  103 + deduction: [
  104 + { required: true, message: this.$t('workType.validate.deduction'), trigger: 'change' }
  105 + ],
  106 + smsWay: [
  107 + { required: true, message: this.$t('workType.validate.smsWay'), trigger: 'change' }
  108 + ]
  109 + }
  110 + }
  111 + },
  112 + methods: {
  113 + open() {
  114 + this.visible = true
  115 + },
  116 + handleClose() {
  117 + this.$refs.form.resetFields()
  118 + },
  119 + handleSubmit() {
  120 + this.$refs.form.validate(async valid => {
  121 + if (valid) {
  122 + try {
  123 + const params = {
  124 + ...this.formData,
  125 + communityId: getCommunityId()
  126 + }
  127 + await saveWorkType(params)
  128 + this.$message.success(this.$t('workType.add.success'))
  129 + this.visible = false
  130 + this.$emit('success')
  131 + } catch (error) {
  132 + this.$message.error(error.message || this.$t('workType.add.error'))
  133 + }
  134 + }
  135 + })
  136 + }
  137 + }
  138 +}
  139 +</script>
  140 +
  141 +<style scoped>
  142 +.el-form-item__tip {
  143 + font-size: 12px;
  144 + color: #999;
  145 + margin-top: 5px;
  146 +}
  147 +</style>
0 \ No newline at end of file 148 \ No newline at end of file
src/components/oa/deleteWork.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('startWork.delete.title')"
  4 + :visible.sync="visible"
  5 + width="30%"
  6 + @close="handleClose"
  7 + >
  8 + <div class="delete-content">
  9 + <p>{{ $t('startWork.delete.confirm') }}</p>
  10 + </div>
  11 + <span slot="footer" class="dialog-footer">
  12 + <el-button @click="handleClose">{{ $t('common.cancel') }}</el-button>
  13 + <el-button type="primary" @click="handleConfirm">{{ $t('common.confirm') }}</el-button>
  14 + </span>
  15 + </el-dialog>
  16 +</template>
  17 +
  18 +<script>
  19 +import { deleteWorkPool } from '@/api/oa/startWorkApi'
  20 +import { getCommunityId } from '@/api/community/communityApi'
  21 +
  22 +export default {
  23 + name: 'DeleteWork',
  24 + data() {
  25 + return {
  26 + visible: false,
  27 + currentWork: null,
  28 + communityId: ''
  29 + }
  30 + },
  31 + created() {
  32 + this.communityId = getCommunityId()
  33 + },
  34 + methods: {
  35 + open(work) {
  36 + this.currentWork = work
  37 + this.visible = true
  38 + },
  39 + handleClose() {
  40 + this.visible = false
  41 + this.currentWork = null
  42 + },
  43 + async handleConfirm() {
  44 + try {
  45 + await deleteWorkPool({
  46 + workId: this.currentWork.workId,
  47 + communityId: this.communityId
  48 + })
  49 + this.$message.success(this.$t('startWork.delete.success'))
  50 + this.$emit('success')
  51 + this.handleClose()
  52 + } catch (error) {
  53 + this.$message.error(this.$t('startWork.delete.error'))
  54 + }
  55 + }
  56 + }
  57 +}
  58 +</script>
  59 +
  60 +<style lang="scss" scoped>
  61 +.delete-content {
  62 + text-align: center;
  63 + font-size: 16px;
  64 + padding: 20px 0;
  65 +}
  66 +</style>
0 \ No newline at end of file 67 \ No newline at end of file
src/components/oa/deleteWorkType.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('workType.delete.title')"
  4 + :visible.sync="visible"
  5 + width="30%"
  6 + @close="handleClose"
  7 + >
  8 + <div class="delete-content">
  9 + <i class="el-icon-warning" style="color:#E6A23C;font-size:24px;"></i>
  10 + <span style="margin-left:10px;">{{ $t('workType.delete.confirm') }}</span>
  11 + </div>
  12 +
  13 + <span slot="footer" class="dialog-footer">
  14 + <el-button @click="visible = false">{{ $t('common.cancel') }}</el-button>
  15 + <el-button type="primary" @click="handleConfirm">{{ $t('common.confirm') }}</el-button>
  16 + </span>
  17 + </el-dialog>
  18 +</template>
  19 +
  20 +<script>
  21 +import { deleteWorkType } from '@/api/oa/workTypeApi'
  22 +import { getCommunityId } from '@/api/community/communityApi'
  23 +
  24 +export default {
  25 + name: 'DeleteWorkType',
  26 + data() {
  27 + return {
  28 + visible: false,
  29 + currentRow: null
  30 + }
  31 + },
  32 + methods: {
  33 + open(row) {
  34 + this.currentRow = row
  35 + this.visible = true
  36 + },
  37 + handleClose() {
  38 + this.currentRow = null
  39 + },
  40 + async handleConfirm() {
  41 + try {
  42 + const params = {
  43 + wtId: this.currentRow.wtId,
  44 + communityId: getCommunityId()
  45 + }
  46 + await deleteWorkType(params)
  47 + this.$message.success(this.$t('workType.delete.success'))
  48 + this.visible = false
  49 + this.$emit('success')
  50 + } catch (error) {
  51 + this.$message.error(error.message || this.$t('workType.delete.error'))
  52 + }
  53 + }
  54 + }
  55 +}
  56 +</script>
  57 +
  58 +<style scoped>
  59 +.delete-content {
  60 + display: flex;
  61 + align-items: center;
  62 + justify-content: center;
  63 + padding: 20px 0;
  64 +}
  65 +</style>
0 \ No newline at end of file 66 \ No newline at end of file
src/components/oa/editWorkType.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('workType.edit.title')"
  4 + :visible.sync="visible"
  5 + width="50%"
  6 + @close="handleClose"
  7 + >
  8 + <el-form ref="form" :model="formData" :rules="rules" label-width="120px">
  9 + <el-form-item :label="$t('workType.form.typeName')" prop="typeName">
  10 + <el-input
  11 + v-model.trim="formData.typeName"
  12 + :placeholder="$t('workType.placeholder.typeName')"
  13 + />
  14 + </el-form-item>
  15 +
  16 + <el-form-item :label="$t('workType.form.deduction')" prop="deduction">
  17 + <el-select
  18 + v-model="formData.deduction"
  19 + :placeholder="$t('workType.placeholder.deduction')"
  20 + style="width:100%"
  21 + >
  22 + <el-option
  23 + v-for="item in deductionOptions"
  24 + :key="item.value"
  25 + :label="item.label"
  26 + :value="item.value"
  27 + />
  28 + </el-select>
  29 + <div class="el-form-item__tip" v-if="formData.deduction === 'Y'">
  30 + {{ $t('workType.tip.deduction') }}
  31 + </div>
  32 + </el-form-item>
  33 +
  34 + <el-form-item :label="$t('workType.form.smsWay')" prop="smsWay">
  35 + <el-select
  36 + v-model="formData.smsWay"
  37 + :placeholder="$t('workType.placeholder.smsWay')"
  38 + style="width:100%"
  39 + >
  40 + <el-option
  41 + v-for="item in smsWayOptions"
  42 + :key="item.value"
  43 + :label="item.label"
  44 + :value="item.value"
  45 + />
  46 + </el-select>
  47 + </el-form-item>
  48 +
  49 + <el-form-item :label="$t('workType.form.remark')">
  50 + <el-input
  51 + v-model.trim="formData.remark"
  52 + type="textarea"
  53 + :rows="3"
  54 + :placeholder="$t('workType.placeholder.remark')"
  55 + maxlength="500"
  56 + show-word-limit
  57 + />
  58 + </el-form-item>
  59 + </el-form>
  60 +
  61 + <span slot="footer" class="dialog-footer">
  62 + <el-button @click="visible = false">{{ $t('common.cancel') }}</el-button>
  63 + <el-button type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</el-button>
  64 + </span>
  65 + </el-dialog>
  66 +</template>
  67 +
  68 +<script>
  69 +import { updateWorkType } from '@/api/oa/workTypeApi'
  70 +import { getCommunityId } from '@/api/community/communityApi'
  71 +
  72 +export default {
  73 + name: 'EditWorkType',
  74 + data() {
  75 + return {
  76 + visible: false,
  77 + formData: {
  78 + wtId: '',
  79 + typeName: '',
  80 + deduction: '',
  81 + smsWay: '',
  82 + remark: ''
  83 + },
  84 + deductionOptions: [
  85 + { value: 'Y', label: this.$t('workType.yes') },
  86 + { value: 'N', label: this.$t('workType.no') }
  87 + ],
  88 + smsWayOptions: [
  89 + { value: 'WECHAT', label: this.$t('workType.wechat') },
  90 + { value: 'ALI_SMS', label: this.$t('workType.aliSms') },
  91 + { value: 'WORK_LICENSE', label: this.$t('workType.workLicense') }
  92 + ],
  93 + rules: {
  94 + typeName: [
  95 + { required: true, message: this.$t('workType.validate.typeName'), trigger: 'blur' },
  96 + { max: 200, message: this.$t('workType.validate.typeNameMax'), trigger: 'blur' }
  97 + ],
  98 + deduction: [
  99 + { required: true, message: this.$t('workType.validate.deduction'), trigger: 'change' }
  100 + ],
  101 + smsWay: [
  102 + { required: true, message: this.$t('workType.validate.smsWay'), trigger: 'change' }
  103 + ],
  104 + wtId: [
  105 + { required: true, message: this.$t('workType.validate.wtId'), trigger: 'blur' }
  106 + ]
  107 + }
  108 + }
  109 + },
  110 + methods: {
  111 + open(row) {
  112 + this.formData = {
  113 + wtId: row.wtId,
  114 + typeName: row.typeName,
  115 + deduction: row.deduction,
  116 + smsWay: row.smsWay,
  117 + remark: row.remark
  118 + }
  119 + this.visible = true
  120 + },
  121 + handleClose() {
  122 + this.$refs.form.resetFields()
  123 + },
  124 + handleSubmit() {
  125 + this.$refs.form.validate(async valid => {
  126 + if (valid) {
  127 + try {
  128 + const params = {
  129 + ...this.formData,
  130 + communityId: getCommunityId()
  131 + }
  132 + await updateWorkType(params)
  133 + this.$message.success(this.$t('workType.edit.success'))
  134 + this.visible = false
  135 + this.$emit('success')
  136 + } catch (error) {
  137 + this.$message.error(error.message || this.$t('workType.edit.error'))
  138 + }
  139 + }
  140 + })
  141 + }
  142 + }
  143 +}
  144 +</script>
  145 +
  146 +<style scoped>
  147 +.el-form-item__tip {
  148 + font-size: 12px;
  149 + color: #999;
  150 + margin-top: 5px;
  151 +}
  152 +</style>
0 \ No newline at end of file 153 \ No newline at end of file
src/components/oa/orgTreeShow.vue
1 <template> 1 <template>
2 - <div class="org-tree-container"> 2 + <div class="org-tree-show">
3 <el-tree 3 <el-tree
4 ref="orgTree" 4 ref="orgTree"
5 - :data="orgData"  
6 - :props="defaultProps" 5 + :data="orgTreeShowInfo.orgs"
7 node-key="id" 6 node-key="id"
8 - default-expand-all  
9 - highlight-current  
10 - :expand-on-click-node="false" 7 + :props="defaultProps"
  8 + :default-expand-all="true"
  9 + :highlight-current="true"
11 @node-click="handleNodeClick" 10 @node-click="handleNodeClick"
12 - ></el-tree> 11 + />
13 </div> 12 </div>
14 </template> 13 </template>
15 14
16 <script> 15 <script>
17 -import { listOrgTree } from '@/api/oa/addAttendanceClassesStaffApi' 16 +import { listOrgTree } from '@/api/oa/editWorkApi'
18 import { getCommunityId } from '@/api/community/communityApi' 17 import { getCommunityId } from '@/api/community/communityApi'
19 18
20 export default { 19 export default {
21 name: 'OrgTreeShow', 20 name: 'OrgTreeShow',
  21 + props: {
  22 + callBackListener: {
  23 + type: String,
  24 + required: true
  25 + }
  26 + },
22 data() { 27 data() {
23 return { 28 return {
24 - orgData: [], 29 + orgTreeShowInfo: {
  30 + orgs: [],
  31 + orgId: '',
  32 + curOrg: {}
  33 + },
25 defaultProps: { 34 defaultProps: {
26 children: 'children', 35 children: 'children',
27 label: 'text' 36 label: 'text'
28 - },  
29 - communityId: getCommunityId() 37 + }
30 } 38 }
31 }, 39 },
  40 + mounted() {
  41 + this._loadOrgsShow()
  42 + },
32 methods: { 43 methods: {
33 - async refreshTree() { 44 + refreshTree() {
  45 + this._loadOrgsShow()
  46 + },
  47 + async _loadOrgsShow() {
34 try { 48 try {
35 const params = { 49 const params = {
36 - communityId: this.communityId 50 + communityId: getCommunityId()
37 } 51 }
38 - const res = await listOrgTree(params)  
39 - this.orgData = res.data 52 + const { data } = await listOrgTree(params)
  53 + this.orgTreeShowInfo.orgs = data
40 } catch (error) { 54 } catch (error) {
41 - this.$message.error(this.$t('orgTree.loadFailed')) 55 + console.error('加载组织树失败:', error)
42 } 56 }
43 }, 57 },
44 handleNodeClick(data) { 58 handleNodeClick(data) {
45 - this.$emit('switchOrg', { 59 + this.orgTreeShowInfo.curOrg = data
  60 + this.orgTreeShowInfo.curOrg.orgId = data.id
  61 + this.$emit(this.callBackListener, 'switchOrg', {
46 orgId: data.id, 62 orgId: data.id,
47 orgName: data.text 63 orgName: data.text
48 }) 64 })
49 } 65 }
50 - },  
51 - mounted() {  
52 - this.refreshTree()  
53 } 66 }
54 } 67 }
55 </script> 68 </script>
56 69
57 <style lang="scss" scoped> 70 <style lang="scss" scoped>
58 -.org-tree-container {  
59 - height: 500px;  
60 - overflow-y: auto;  
61 - padding: 10px;  
62 - 71 +.org-tree-show {
63 ::v-deep .el-tree { 72 ::v-deep .el-tree {
64 - background-color: transparent;  
65 - 73 + background: transparent;
66 .el-tree-node__content { 74 .el-tree-node__content {
67 height: 36px; 75 height: 36px;
68 -  
69 &:hover { 76 &:hover {
70 background-color: #f5f7fa; 77 background-color: #f5f7fa;
71 } 78 }
72 } 79 }
73 -  
74 .is-current > .el-tree-node__content { 80 .is-current > .el-tree-node__content {
75 background-color: #ecf5ff; 81 background-color: #ecf5ff;
  82 + color: #409eff;
76 } 83 }
77 } 84 }
78 } 85 }
src/components/oa/selectStaff.vue
1 <template> 1 <template>
2 - <el-dialog  
3 - :title="$t('selectStaff.title')"  
4 - :visible.sync="visible" 2 + <el-dialog
  3 + :title="$t('selectStaff.title')"
  4 + :visible.sync="visible"
5 width="70%" 5 width="70%"
6 - :close-on-click-modal="false" 6 + top="5vh"
7 > 7 >
8 <el-row :gutter="20"> 8 <el-row :gutter="20">
9 <el-col :span="12" class="border-right"> 9 <el-col :span="12" class="border-right">
10 - <div class="text-center mb-20">  
11 - <h4>{{ $t('selectStaff.orgInfo') }}</h4> 10 + <div class="text-center section-title">
  11 + {{ $t('selectStaff.orgInfo') }}
  12 + </div>
  13 + <div class="org-tree-container">
  14 + <org-tree-show
  15 + ref="orgTree"
  16 + :call-back-listener="'selectStaff'"
  17 + />
12 </div> 18 </div>
13 - <org-tree-show  
14 - ref="orgTree"  
15 - @switchOrg="handleSwitchOrg"  
16 - ></org-tree-show>  
17 </el-col> 19 </el-col>
18 -  
19 <el-col :span="12"> 20 <el-col :span="12">
20 - <div class="text-center mb-20">  
21 - <h4>{{ $t('selectStaff.staffInfo') }}</h4> 21 + <div class="text-center section-title">
  22 + {{ $t('selectStaff.staffInfo') }}
22 </div> 23 </div>
23 <div class="staff-list"> 24 <div class="staff-list">
24 - <div  
25 - v-for="(staff, index) in staffList"  
26 - :key="index"  
27 - class="staff-item"  
28 - :class="{ 'selected': currentStaffId === staff.staffId }"  
29 - @click="handleSelectStaff(staff)" 25 + <div
  26 + v-for="(item, index) in selectStaffInfo.staffs"
  27 + :key="index"
  28 + class="staff-item"
  29 + :class="{ 'selected': selectStaffInfo.curStaffId === item.staffId }"
  30 + @click="_changeStaff(item)"
30 > 31 >
31 <div> 32 <div>
32 <i class="el-icon-user"></i> 33 <i class="el-icon-user"></i>
33 - {{ staff.name }} 34 + {{ item.name }}
34 </div> 35 </div>
35 - <div>{{ staff.tel }}</div> 36 + <div>{{ item.tel }}</div>
36 </div> 37 </div>
37 </div> 38 </div>
38 </el-col> 39 </el-col>
39 </el-row> 40 </el-row>
40 -  
41 - <div  
42 - v-if="staffData.from === 'bpmn' || staffData.from === 'purchase' || staffData.from === 'contract'"  
43 - slot="footer" 41 + <div
  42 + v-if="selectStaffInfo.staff.from === 'bpmn' ||
  43 + selectStaffInfo.staff.from === 'purchase' ||
  44 + selectStaffInfo.staff.from === 'contract'"
  45 + slot="footer"
44 class="dialog-footer" 46 class="dialog-footer"
45 > 47 >
46 - <el-button @click="handleFirstUser">{{ $t('selectStaff.submitter') }}</el-button>  
47 - <el-button @click="handleCustomUser">{{ $t('selectStaff.dynamicAssign') }}</el-button> 48 + <el-button @click="_firstUser">
  49 + {{ $t('selectStaff.submitter') }}
  50 + </el-button>
  51 + <el-button @click="_customUser">
  52 + {{ $t('selectStaff.dynamicAssign') }}
  53 + </el-button>
48 </div> 54 </div>
49 </el-dialog> 55 </el-dialog>
50 </template> 56 </template>
51 57
52 <script> 58 <script>
  59 +import { queryStaffInfos } from '@/api/oa/editWorkApi'
53 import OrgTreeShow from './OrgTreeShow' 60 import OrgTreeShow from './OrgTreeShow'
54 -import { queryStaffInfos } from '@/api/oa/addAttendanceClassesStaffApi'  
55 61
56 export default { 62 export default {
57 name: 'SelectStaff', 63 name: 'SelectStaff',
@@ -61,95 +67,108 @@ export default { @@ -61,95 +67,108 @@ export default {
61 data() { 67 data() {
62 return { 68 return {
63 visible: false, 69 visible: false,
64 - staffData: {},  
65 - staffList: [],  
66 - currentStaffId: '',  
67 - currentOrgId: '' 70 + selectStaffInfo: {
  71 + staffs: [],
  72 + curStaffId: '',
  73 + curStaffName: '',
  74 + staff: {}
  75 + }
68 } 76 }
69 }, 77 },
70 methods: { 78 methods: {
71 open(staff) { 79 open(staff) {
72 - this.staffData = staff 80 + this.selectStaffInfo.staff = staff || {}
73 this.visible = true 81 this.visible = true
74 this.$nextTick(() => { 82 this.$nextTick(() => {
75 this.$refs.orgTree.refreshTree() 83 this.$refs.orgTree.refreshTree()
76 }) 84 })
77 }, 85 },
78 - async handleSwitchOrg(org) {  
79 - this.currentOrgId = org.orgId 86 + _changeStaff(item) {
  87 + this.selectStaffInfo.staff.staffId = item.userId
  88 + this.selectStaffInfo.staff.staffName = item.userName
  89 + this.selectStaffInfo.staff.staffTel = item.tel
  90 + this.visible = false
  91 + if (typeof this.selectStaffInfo.staff.call === 'function') {
  92 + this.selectStaffInfo.staff.call(this.selectStaffInfo.staff)
  93 + }
  94 + },
  95 + async loadStaff(org) {
80 try { 96 try {
81 const params = { 97 const params = {
82 page: 1, 98 page: 1,
  99 + rows: 50,
83 row: 50, 100 row: 50,
84 orgId: org.orgId 101 orgId: org.orgId
85 } 102 }
86 - const res = await queryStaffInfos(params)  
87 - this.staffList = res.data.staffs  
88 - if (this.staffList.length > 0) {  
89 - this.currentStaffId = this.staffList[0].staffId 103 + const { data } = await queryStaffInfos(params)
  104 + this.selectStaffInfo.staffs = data.staffs
  105 + if (data.staffs.length > 0) {
  106 + this.selectStaffInfo.curStaffId = data.staffs[0].orgId
90 } 107 }
91 } catch (error) { 108 } catch (error) {
92 - this.$message.error(this.$t('selectStaff.loadStaffFailed')) 109 + console.error('加载员工失败:', error)
93 } 110 }
94 }, 111 },
95 - handleSelectStaff(staff) {  
96 - this.$emit('change', {  
97 - staffId: staff.userId,  
98 - staffName: staff.userName,  
99 - staffTel: staff.tel  
100 - })  
101 - this.visible = false  
102 - },  
103 - handleFirstUser() {  
104 - this.$emit('change', {  
105 - staffId: '${startUserId}',  
106 - staffName: this.$t('selectStaff.submitter')  
107 - }) 112 + _firstUser() {
  113 + this.selectStaffInfo.staff.staffId = '${startUserId}'
  114 + this.selectStaffInfo.staff.staffName = this.$t('selectStaff.submitter')
108 this.visible = false 115 this.visible = false
  116 + if (typeof this.selectStaffInfo.staff.call === 'function') {
  117 + this.selectStaffInfo.staff.call(this.selectStaffInfo.staff)
  118 + }
109 }, 119 },
110 - handleCustomUser() {  
111 - this.$emit('change', {  
112 - staffId: '${nextUserId}',  
113 - staffName: this.$t('selectStaff.dynamicAssign')  
114 - }) 120 + _customUser() {
  121 + this.selectStaffInfo.staff.staffId = '${nextUserId}'
  122 + this.selectStaffInfo.staff.staffName = this.$t('selectStaff.dynamicAssign')
115 this.visible = false 123 this.visible = false
  124 + if (typeof this.selectStaffInfo.staff.call === 'function') {
  125 + this.selectStaffInfo.staff.call(this.selectStaffInfo.staff)
  126 + }
116 } 127 }
  128 + },
  129 + created() {
  130 + this.$on('selectStaff', 'switchOrg', this.loadStaff)
117 } 131 }
118 } 132 }
119 </script> 133 </script>
120 134
121 <style lang="scss" scoped> 135 <style lang="scss" scoped>
122 .border-right { 136 .border-right {
123 - border-right: 1px solid #ebeef5; 137 + border-right: 1px solid #eee;
124 } 138 }
125 139
126 -.text-center {  
127 - text-align: center; 140 +.section-title {
  141 + font-weight: bold;
  142 + margin-bottom: 15px;
128 } 143 }
129 144
130 -.mb-20 {  
131 - margin-bottom: 20px; 145 +.org-tree-container {
  146 + padding: 10px;
  147 + height: 500px;
  148 + overflow-y: auto;
132 } 149 }
133 150
134 .staff-list { 151 .staff-list {
135 - max-height: 500px; 152 + padding: 10px;
  153 + height: 500px;
136 overflow-y: auto; 154 overflow-y: auto;
137 } 155 }
138 156
139 .staff-item { 157 .staff-item {
140 padding: 10px; 158 padding: 10px;
141 margin-bottom: 5px; 159 margin-bottom: 5px;
142 - border: 1px solid #ebeef5;  
143 border-radius: 4px; 160 border-radius: 4px;
144 cursor: pointer; 161 cursor: pointer;
145 -  
146 &:hover { 162 &:hover {
147 background-color: #f5f7fa; 163 background-color: #f5f7fa;
148 } 164 }
149 -  
150 &.selected { 165 &.selected {
151 background-color: #ecf5ff; 166 background-color: #ecf5ff;
152 - border-color: #d9ecff; 167 + color: #409eff;
153 } 168 }
154 } 169 }
  170 +
  171 +.dialog-footer {
  172 + text-align: right;
  173 +}
155 </style> 174 </style>
156 \ No newline at end of file 175 \ No newline at end of file
src/components/oa/textarea.vue 0 → 100644
  1 +<template>
  2 + <div class="textarea-container">
  3 + <div ref="editor" class="editor"></div>
  4 + </div>
  5 +</template>
  6 +
  7 +<script>
  8 +import { uploadImage } from '@/api/oa/editWorkApi'
  9 +import { getCommunityId } from '@/api/community/communityApi'
  10 +
  11 +export default {
  12 + name: 'TextareaEditor',
  13 + data() {
  14 + return {
  15 + content: ''
  16 + }
  17 + },
  18 + mounted() {
  19 + this._initEditor()
  20 + },
  21 + methods: {
  22 + _initEditor() {
  23 + $(this.$refs.editor).summernote({
  24 + lang: 'zh-CN',
  25 + height: 300,
  26 + placeholder: this.$t('textarea.placeholder'),
  27 + callbacks: {
  28 + onImageUpload: (files) => {
  29 + this._uploadImage(files[0])
  30 + },
  31 + onChange: (contents) => {
  32 + this.content = contents.replace(/&nbsp;/g, '').replace(/\s/gi, '')
  33 + this.$emit('change', this.content)
  34 + }
  35 + },
  36 + toolbar: [
  37 + ['style', ['style']],
  38 + ['font', ['bold', 'italic', 'underline', 'clear']],
  39 + ['fontname', ['fontname']],
  40 + ['color', ['color']],
  41 + ['para', ['ul', 'ol', 'paragraph']],
  42 + ['height', ['height']],
  43 + ['table', ['table']],
  44 + ['insert', ['link', 'picture']]
  45 + ]
  46 + })
  47 + },
  48 + setText(content) {
  49 + $(this.$refs.editor).summernote('code', content)
  50 + },
  51 + async _uploadImage(file) {
  52 + const formData = new FormData()
  53 + formData.append('uploadFile', file)
  54 + formData.append('communityId', getCommunityId())
  55 +
  56 + try {
  57 + const res = await uploadImage(formData)
  58 + $(this.$refs.editor).summernote('insertImage', res.url)
  59 + } catch (error) {
  60 + console.error('上传图片失败:', error)
  61 + this.$message.error(this.$t('textarea.uploadFailed'))
  62 + }
  63 + }
  64 + }
  65 +}
  66 +</script>
  67 +
  68 +<style lang="scss" scoped>
  69 +.textarea-container {
  70 + .editor {
  71 + width: 100%;
  72 + }
  73 +}
  74 +</style>
0 \ No newline at end of file 75 \ No newline at end of file
src/components/oa/uploadFile.vue 0 → 100644
  1 +<template>
  2 + <div class="upload-file-container">
  3 + <el-progress
  4 + v-if="uploadFileInfo.progress > 0"
  5 + :percentage="uploadFileInfo.progress"
  6 + :stroke-width="2"
  7 + />
  8 + <div v-if="uploadFileInfo.progress > 0" class="file-name">
  9 + {{ uploadFileInfo.fileName }}
  10 + </div>
  11 + <div>
  12 + <el-button
  13 + type="primary"
  14 + size="small"
  15 + @click="_uploadFile"
  16 + >
  17 + {{ $t('uploadFile.upload') }}
  18 + </el-button>
  19 + <input
  20 + ref="fileInput"
  21 + type="file"
  22 + class="file-input"
  23 + accept="*"
  24 + capture="camcorder"
  25 + @change="_chooseFile"
  26 + >
  27 + </div>
  28 + </div>
  29 +</template>
  30 +
  31 +<script>
  32 +import { uploadFile } from '@/api/oa/editWorkApi'
  33 +
  34 +export default {
  35 + name: 'UploadFile',
  36 + props: {
  37 + callBackListener: {
  38 + type: String,
  39 + default: ''
  40 + },
  41 + callBackFunction: {
  42 + type: String,
  43 + default: ''
  44 + }
  45 + },
  46 + data() {
  47 + return {
  48 + uploadFileInfo: {
  49 + vedio: {},
  50 + fileName: '',
  51 + realFileName: '',
  52 + progress: 0
  53 + }
  54 + }
  55 + },
  56 + watch: {
  57 + uploadFileInfo: {
  58 + deep: true,
  59 + handler(newVal) {
  60 + this.$emit(this.callBackListener, this.callBackFunction, newVal)
  61 + }
  62 + }
  63 + },
  64 + methods: {
  65 + notifyVedio(fileName) {
  66 + this.uploadFileInfo = {
  67 + vedio: {},
  68 + fileName: fileName,
  69 + realFileName: fileName,
  70 + progress: 100
  71 + }
  72 + },
  73 + clearVedio() {
  74 + this.uploadFileInfo = {
  75 + vedio: {},
  76 + fileName: '',
  77 + realFileName: '',
  78 + progress: 0
  79 + }
  80 + },
  81 + _uploadFile() {
  82 + this.$refs.fileInput.click()
  83 + },
  84 + _chooseFile(event) {
  85 + const files = event.target.files
  86 + if (files && files.length > 0) {
  87 + const file = files[0]
  88 + if (file.size > 1024 * 1024 * 20) {
  89 + this.$message.error(this.$t('uploadFile.sizeLimit'))
  90 + return false
  91 + }
  92 + this.uploadFileInfo.fileName = file.name
  93 + this._doUploadFile(file)
  94 + }
  95 + },
  96 + async _doUploadFile(file) {
  97 + const formData = new FormData()
  98 + formData.append('uploadFile', file)
  99 +
  100 + try {
  101 + const res = await uploadFile(formData, {
  102 + onUploadProgress: (progressEvent) => {
  103 + const rate = progressEvent.loaded / progressEvent.total
  104 + if (rate < 0.9) {
  105 + this.uploadFileInfo.progress = Math.floor(rate * 100)
  106 + }
  107 + }
  108 + })
  109 +
  110 + this.uploadFileInfo.progress = 100
  111 + this.$message.success(this.$t('uploadFile.success'))
  112 + this.uploadFileInfo.fileName = res.fileName
  113 + this.uploadFileInfo.realFileName = res.realFileName
  114 + this.$emit(this.callBackListener, this.callBackFunction, res)
  115 + } catch (error) {
  116 + console.error('上传失败:', error)
  117 + this.$message.error(this.$t('uploadFile.failed'))
  118 + }
  119 + }
  120 + }
  121 +}
  122 +</script>
  123 +
  124 +<style lang="scss" scoped>
  125 +.upload-file-container {
  126 + .file-name {
  127 + margin-bottom: 5px;
  128 + }
  129 +
  130 + .file-input {
  131 + display: none;
  132 + }
  133 +}
  134 +</style>
0 \ No newline at end of file 135 \ No newline at end of file
src/components/system/uploadFile.vue renamed to src/components/upload/uploadFile.vue
@@ -71,7 +71,7 @@ export default { @@ -71,7 +71,7 @@ export default {
71 } 71 }
72 } 72 }
73 73
74 - const { data } = await uploadFile(formData, config) 74 + const data = await uploadFile(formData, config)
75 this.progress = 100 75 this.progress = 100
76 this.realFileName = data.realFileName 76 this.realFileName = data.realFileName
77 this.$emit('notify', data) 77 this.$emit('notify', data)
src/i18n/oaI18n.js
@@ -37,7 +37,10 @@ import { messages as addAttendanceClassesStaffMessages } from &#39;../views/oa/addAt @@ -37,7 +37,10 @@ import { messages as addAttendanceClassesStaffMessages } from &#39;../views/oa/addAt
37 import { messages as monthAttendanceManageMessages } from '../views/oa/monthAttendanceManageLang' 37 import { messages as monthAttendanceManageMessages } from '../views/oa/monthAttendanceManageLang'
38 import { messages as staffAttendanceManageMessages } from '../views/oa/staffAttendanceManageLang' 38 import { messages as staffAttendanceManageMessages } from '../views/oa/staffAttendanceManageLang'
39 import { messages as attendanceLogManageMessages } from '../views/oa/attendanceLogManageLang' 39 import { messages as attendanceLogManageMessages } from '../views/oa/attendanceLogManageLang'
40 - 40 +import { messages as workTypeMessages } from '../views/oa/workTypeLang'
  41 +import { messages as startWorkMessages } from '../views/oa/startWorkLang'
  42 +import { messages as addWorkMessages } from '../views/oa/addWorkLang'
  43 +import { messages as editWorkMessages } from '../views/oa/editWorkLang'
41 export const messages ={ 44 export const messages ={
42 en:{ 45 en:{
43 ...activitiesTypeManageMessages.en, 46 ...activitiesTypeManageMessages.en,
@@ -78,6 +81,10 @@ export const messages ={ @@ -78,6 +81,10 @@ export const messages ={
78 ...monthAttendanceManageMessages.en, 81 ...monthAttendanceManageMessages.en,
79 ...staffAttendanceManageMessages.en, 82 ...staffAttendanceManageMessages.en,
80 ...attendanceLogManageMessages.en, 83 ...attendanceLogManageMessages.en,
  84 + ...workTypeMessages.en,
  85 + ...startWorkMessages.en,
  86 + ...addWorkMessages.en,
  87 + ...editWorkMessages.en,
81 }, 88 },
82 zh:{ 89 zh:{
83 ...activitiesTypeManageMessages.zh, 90 ...activitiesTypeManageMessages.zh,
@@ -118,5 +125,9 @@ export const messages ={ @@ -118,5 +125,9 @@ export const messages ={
118 ...monthAttendanceManageMessages.zh, 125 ...monthAttendanceManageMessages.zh,
119 ...staffAttendanceManageMessages.zh, 126 ...staffAttendanceManageMessages.zh,
120 ...attendanceLogManageMessages.zh, 127 ...attendanceLogManageMessages.zh,
  128 + ...workTypeMessages.zh,
  129 + ...startWorkMessages.zh,
  130 + ...addWorkMessages.zh,
  131 + ...editWorkMessages.zh,
121 } 132 }
122 } 133 }
123 \ No newline at end of file 134 \ No newline at end of file
src/router/oaRouter.js
@@ -165,13 +165,33 @@ export default [ @@ -165,13 +165,33 @@ export default [
165 component: () => import('@/views/oa/monthAttendanceManageList.vue') 165 component: () => import('@/views/oa/monthAttendanceManageList.vue')
166 }, 166 },
167 { 167 {
168 - path:'/pages/property/staffAttendanceManage',  
169 - name:'/pages/property/staffAttendanceManage', 168 + path: '/pages/property/staffAttendanceManage',
  169 + name: '/pages/property/staffAttendanceManage',
170 component: () => import('@/views/oa/staffAttendanceManageList.vue') 170 component: () => import('@/views/oa/staffAttendanceManageList.vue')
  171 + },
  172 + {
  173 + path: '/pages/property/attendanceLogManage',
  174 + name: '/pages/property/attendanceLogManage',
  175 + component: () => import('@/views/oa/attendanceLogManageList.vue')
  176 + },
  177 + {
  178 + path: '/pages/oa/workType',
  179 + name: '/pages/oa/workType',
  180 + component: () => import('@/views/oa/workTypeList.vue')
  181 + },
  182 + {
  183 + path: '/pages/oa/startWork',
  184 + name: '/pages/oa/startWork',
  185 + component: () => import('@/views/oa/startWorkList.vue')
  186 + },
  187 + {
  188 + path:'/views/oa/addWork',
  189 + name:'/views/oa/addWork',
  190 + component: () => import('@/views/oa/addWorkList.vue')
171 }, 191 },
172 { 192 {
173 - path:'/pages/property/attendanceLogManage',  
174 - name:'/pages/property/attendanceLogManage',  
175 - component: () => import('@/views/oa/attendanceLogManageList.vue') 193 + path:'/views/oa/editWork',
  194 + name:'/views/oa/editWork',
  195 + component: () => import('@/views/oa/editWorkList.vue')
176 }, 196 },
177 ] 197 ]
178 \ No newline at end of file 198 \ No newline at end of file
src/views/oa/addWorkLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + addWork: {
  4 + title: 'Create Work Order',
  5 + workName: 'Title',
  6 + workNamePlaceholder: 'Required, please enter title',
  7 + workType: 'Work Order Type',
  8 + workTypePlaceholder: 'Required, please select work order type',
  9 + processor: 'Processor',
  10 + cc: 'CC',
  11 + choose: 'Choose',
  12 + workCycle: 'Work Order Flag',
  13 + workCyclePlaceholder: 'Required, please select work order flag',
  14 + onceWork: 'One-time Work Order',
  15 + periodWork: 'Periodic Work Order',
  16 + attachment: 'Attachment',
  17 + startTime: 'Start Time',
  18 + startTimePlaceholder: 'Required, please enter start time',
  19 + endTime: 'End Time',
  20 + endTimePlaceholder: 'Required, please enter end time',
  21 + period: 'Period',
  22 + periodPlaceholder: 'Required, please select task period',
  23 + monthDay: 'Month/Day',
  24 + week: 'By Week',
  25 + hours: 'Work Order',
  26 + hoursPlaceholder: 'Required, please enter hours',
  27 + hoursUnit: 'hours to complete',
  28 + month: 'Month',
  29 + monthUnit: 'Month',
  30 + day: 'Day',
  31 + dayUnit: 'Day',
  32 + monday: 'Monday',
  33 + tuesday: 'Tuesday',
  34 + wednesday: 'Wednesday',
  35 + thursday: 'Thursday',
  36 + friday: 'Friday',
  37 + saturday: 'Saturday',
  38 + sunday: 'Sunday',
  39 + content: 'Content',
  40 + contentPlaceholder: 'Required, please enter content',
  41 + addContent: 'Add',
  42 + deleteContent: 'Delete'
  43 + },
  44 + uploadFile: {
  45 + upload: 'Upload Attachment',
  46 + sizeLimit: 'File size cannot exceed 20MB',
  47 + success: 'File uploaded successfully',
  48 + failed: 'File upload failed'
  49 + },
  50 + selectStaff: {
  51 + title: 'Select Staff',
  52 + orgInfo: 'Organization Information',
  53 + staffInfo: 'Staff Information',
  54 + submitter: 'Submitter',
  55 + dynamicAssign: 'Dynamic Assignment'
  56 + }
  57 + },
  58 + zh: {
  59 + addWork: {
  60 + title: '发起工作单',
  61 + workName: '标题',
  62 + workNamePlaceholder: '必填,请填写标题',
  63 + workType: '工作单类型',
  64 + workTypePlaceholder: '必填,请选择工作单类型',
  65 + processor: '处理人',
  66 + cc: '抄送人',
  67 + choose: '选择',
  68 + workCycle: '工作单标识',
  69 + workCyclePlaceholder: '必填,请选择工作单标识',
  70 + onceWork: '一次性工单',
  71 + periodWork: '周期性工单',
  72 + attachment: '附件',
  73 + startTime: '开始时间',
  74 + startTimePlaceholder: '必填,请填写开始时间',
  75 + endTime: '结束时间',
  76 + endTimePlaceholder: '必填,请填写结束时间',
  77 + period: '周期',
  78 + periodPlaceholder: '必填,请选择任务周期',
  79 + monthDay: '月/天',
  80 + week: '按周',
  81 + hours: '工作单',
  82 + hoursPlaceholder: '必填,请输入小时数',
  83 + hoursUnit: '小时内完成',
  84 + month: '月',
  85 + monthUnit: '月',
  86 + day: '日',
  87 + dayUnit: '日',
  88 + monday: '星期一',
  89 + tuesday: '星期二',
  90 + wednesday: '星期三',
  91 + thursday: '星期四',
  92 + friday: '星期五',
  93 + saturday: '星期六',
  94 + sunday: '星期日',
  95 + content: '内容',
  96 + contentPlaceholder: '必填,请输入内容',
  97 + addContent: '添加',
  98 + deleteContent: '删除'
  99 + },
  100 + uploadFile: {
  101 + upload: '上传附件',
  102 + sizeLimit: '文件大小不能超过20MB',
  103 + success: '文件上传成功',
  104 + failed: '文件上传失败'
  105 + },
  106 + selectStaff: {
  107 + title: '选择员工',
  108 + orgInfo: '组织信息',
  109 + staffInfo: '员工信息',
  110 + submitter: '提交者',
  111 + dynamicAssign: '动态指定'
  112 + }
  113 + }
  114 +}
0 \ No newline at end of file 115 \ No newline at end of file
src/views/oa/addWorkList.vue 0 → 100644
  1 +<template>
  2 + <div class="add-work-container">
  3 + <el-card class="box-card">
  4 + <div slot="header" class="clearfix">
  5 + <span>{{ $t('addWork.title') }}</span>
  6 + </div>
  7 +
  8 + <el-form ref="form" :model="addWorkInfo" label-width="120px" label-position="right">
  9 + <el-row :gutter="20">
  10 + <el-col :span="12">
  11 + <el-form-item :label="$t('addWork.workName')" prop="workName">
  12 + <el-input v-model.trim="addWorkInfo.workName" :placeholder="$t('addWork.workNamePlaceholder')"
  13 + maxlength="20" clearable />
  14 + </el-form-item>
  15 + </el-col>
  16 + <el-col :span="12">
  17 + <el-form-item :label="$t('addWork.workType')" prop="wtId">
  18 + <el-select v-model="addWorkInfo.wtId" :placeholder="$t('addWork.workTypePlaceholder')" style="width:100%"
  19 + clearable>
  20 + <el-option v-for="item in addWorkInfo.workTypes" :key="item.wtId" :label="item.typeName"
  21 + :value="item.wtId" />
  22 + </el-select>
  23 + </el-form-item>
  24 + </el-col>
  25 + </el-row>
  26 +
  27 + <el-row :gutter="20">
  28 + <el-col :span="12">
  29 + <el-form-item :label="$t('addWork.processor')">
  30 + <div class="staff-tags">
  31 + <el-tag v-for="(staff, index) in addWorkInfo.staffs" :key="index" closable
  32 + @close="_deleteWorkStaff(staff)">
  33 + {{ staff.staffName }}
  34 + </el-tag>
  35 + <el-button type="text" @click="_chooseWorkStaff">
  36 + <i class="el-icon-search"></i>{{ $t('addWork.choose') }}
  37 + </el-button>
  38 + </div>
  39 + </el-form-item>
  40 + </el-col>
  41 + <el-col :span="12">
  42 + <el-form-item :label="$t('addWork.cc')">
  43 + <div class="staff-tags">
  44 + <el-tag v-for="(staff, index) in addWorkInfo.copyStaffs" :key="index" closable
  45 + @close="_deleteCopyWorkStaff(staff)">
  46 + {{ staff.staffName }}
  47 + </el-tag>
  48 + <el-button type="text" @click="_chooseCopyWorkStaff">
  49 + <i class="el-icon-search"></i>{{ $t('addWork.choose') }}
  50 + </el-button>
  51 + </div>
  52 + </el-form-item>
  53 + </el-col>
  54 + </el-row>
  55 +
  56 + <el-row :gutter="20">
  57 + <el-col :span="12">
  58 + <el-form-item :label="$t('addWork.workCycle')" prop="workCycle">
  59 + <el-select v-model="addWorkInfo.workCycle" :placeholder="$t('addWork.workCyclePlaceholder')"
  60 + style="width:100%" @change="handleWorkCycleChange">
  61 + <el-option :label="$t('addWork.onceWork')" value="1001" />
  62 + <el-option :label="$t('addWork.periodWork')" value="2002" />
  63 + </el-select>
  64 + </el-form-item>
  65 + </el-col>
  66 + <el-col :span="12">
  67 + <el-form-item :label="$t('addWork.attachment')">
  68 + <upload-file ref="uploadFile" @notifyFile="notifyFile" />
  69 + </el-form-item>
  70 + </el-col>
  71 + </el-row>
  72 +
  73 + <el-row :gutter="20">
  74 + <el-col :span="12">
  75 + <el-form-item :label="$t('addWork.startTime')" prop="startTime">
  76 + <el-date-picker v-model="addWorkInfo.startTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
  77 + :placeholder="$t('addWork.startTimePlaceholder')" style="width:100%" />
  78 + </el-form-item>
  79 + </el-col>
  80 + <el-col :span="12">
  81 + <el-form-item :label="$t('addWork.endTime')" prop="endTime">
  82 + <el-date-picker v-model="addWorkInfo.endTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
  83 + :placeholder="$t('addWork.endTimePlaceholder')" style="width:100%" />
  84 + </el-form-item>
  85 + </el-col>
  86 + </el-row>
  87 +
  88 + <template v-if="addWorkInfo.workCycle === '2002'">
  89 + <el-row :gutter="20">
  90 + <el-col :span="12">
  91 + <el-form-item :label="$t('addWork.period')" prop="period">
  92 + <el-select v-model="addWorkInfo.period" :placeholder="$t('addWork.periodPlaceholder')"
  93 + style="width:100%">
  94 + <el-option :label="$t('addWork.monthDay')" value="2020022" />
  95 + <el-option :label="$t('addWork.week')" value="2020023" />
  96 + </el-select>
  97 + </el-form-item>
  98 + </el-col>
  99 + <el-col :span="12">
  100 + <el-form-item :label="$t('addWork.hours')" prop="hours">
  101 + <el-input v-model.trim="addWorkInfo.hours" :placeholder="$t('addWork.hoursPlaceholder')">
  102 + <template slot="append">{{ $t('addWork.hoursUnit') }}</template>
  103 + </el-input>
  104 + </el-form-item>
  105 + </el-col>
  106 + </el-row>
  107 +
  108 + <template v-if="addWorkInfo.period === '2020022'">
  109 + <el-row :gutter="20">
  110 + <el-col :span="24">
  111 + <el-form-item :label="$t('addWork.month')">
  112 + <el-checkbox-group v-model="addWorkInfo.months">
  113 + <el-checkbox v-for="index in 12" :key="index" :label="index">
  114 + {{ index }}{{ $t('addWork.monthUnit') }}
  115 + </el-checkbox>
  116 + </el-checkbox-group>
  117 + </el-form-item>
  118 + </el-col>
  119 + </el-row>
  120 +
  121 + <el-row :gutter="20">
  122 + <el-col :span="24">
  123 + <el-form-item :label="$t('addWork.day')">
  124 + <el-checkbox-group v-model="addWorkInfo.days">
  125 + <el-checkbox v-for="index in 31" :key="index" :label="index">
  126 + {{ index }}{{ $t('addWork.dayUnit') }}
  127 + </el-checkbox>
  128 + </el-checkbox-group>
  129 + </el-form-item>
  130 + </el-col>
  131 + </el-row>
  132 + </template>
  133 +
  134 + <template v-if="addWorkInfo.period === '2020023'">
  135 + <el-row :gutter="20">
  136 + <el-col :span="24">
  137 + <el-form-item :label="$t('addWork.week')">
  138 + <el-checkbox-group v-model="addWorkInfo.workdays">
  139 + <el-checkbox v-for="(day, index) in weekDays" :key="index" :label="index + 1">
  140 + {{ day }}
  141 + </el-checkbox>
  142 + </el-checkbox-group>
  143 + </el-form-item>
  144 + </el-col>
  145 + </el-row>
  146 + </template>
  147 + </template>
  148 +
  149 + <el-row v-for="(item, index) in addWorkInfo.contents" :key="item.id" :gutter="20">
  150 + <el-col :span="18">
  151 + <el-form-item :label="`${$t('addWork.content')}${index + 1}`">
  152 + <el-input v-model="item.content" type="textarea" :rows="5"
  153 + :placeholder="$t('addWork.contentPlaceholder')" />
  154 + </el-form-item>
  155 + </el-col>
  156 + <el-col :span="6" class="flex align-center">
  157 + <el-button v-if="index === 0" type="text" @click="addWorkContent">
  158 + {{ $t('addWork.addContent') }}
  159 + </el-button>
  160 + <el-button v-else type="text" @click="deleteWorkContent(item)">
  161 + {{ $t('addWork.deleteContent') }}
  162 + </el-button>
  163 + </el-col>
  164 + </el-row>
  165 +
  166 + <el-row>
  167 + <el-col :span="24" class="text-right">
  168 + <el-button type="warning" @click="goBack">
  169 + <i class="el-icon-arrow-left"></i>{{ $t('common.back') }}
  170 + </el-button>
  171 + <el-button type="primary" @click="saveWorkPool">
  172 + <i class="el-icon-check"></i>{{ $t('common.submit') }}
  173 + </el-button>
  174 + </el-col>
  175 + </el-row>
  176 + </el-form>
  177 + </el-card>
  178 +
  179 + <select-staff ref="selectStaff" @selectStaff="selectStaff" />
  180 + </div>
  181 +</template>
  182 +
  183 +<script>
  184 +import { saveWorkPool, listWorkType } from '@/api/oa/addWorkApi'
  185 +import { getCommunityId } from '@/api/community/communityApi'
  186 +import SelectStaff from '@/components/staff/SelectStaff'
  187 +import UploadFile from '@/components/upload/uploadFile'
  188 +import { getUuid } from '@/utils/commonUtil'
  189 +import { dateFormat } from '@/utils/dateUtil'
  190 +
  191 +export default {
  192 + name: 'AddWorkList',
  193 + components: {
  194 + SelectStaff,
  195 + UploadFile
  196 + },
  197 + data() {
  198 + return {
  199 + chooseStaffFlag:'1',
  200 + addWorkInfo: {
  201 + workName: '',
  202 + workTypes: [],
  203 + wtId: '',
  204 + workCycle: '1001',
  205 + startTime: '',
  206 + endTime: '',
  207 + staffs: [],
  208 + copyStaffs: [],
  209 + pathUrls: [],
  210 + contents: [],
  211 + period: '',
  212 + months: [],
  213 + days: [],
  214 + workdays: [],
  215 + hours: '24',
  216 + communityId: ''
  217 + },
  218 + weekDays: [
  219 + this.$t('addWork.monday'),
  220 + this.$t('addWork.tuesday'),
  221 + this.$t('addWork.wednesday'),
  222 + this.$t('addWork.thursday'),
  223 + this.$t('addWork.friday'),
  224 + this.$t('addWork.saturday'),
  225 + this.$t('addWork.sunday')
  226 + ]
  227 + }
  228 + },
  229 + created() {
  230 + this.addWorkInfo.communityId = getCommunityId()
  231 + this._listWorkTypes()
  232 + this.addWorkContent()
  233 + this.initDateTime()
  234 + },
  235 + methods: {
  236 + initDateTime() {
  237 + const now = new Date()
  238 + this.addWorkInfo.startTime = dateFormat(now)
  239 + const tomorrow = new Date(now)
  240 + tomorrow.setDate(tomorrow.getDate() + 1)
  241 + this.addWorkInfo.endTime = dateFormat(tomorrow)
  242 + },
  243 + addWorkValidate() {
  244 + return this.$refs.form.validate()
  245 + },
  246 + async saveWorkPool() {
  247 + try {
  248 + const valid = await this.addWorkValidate()
  249 + if (!valid) return
  250 +
  251 + const params = {
  252 + ...this.addWorkInfo,
  253 + }
  254 +
  255 + const res = await saveWorkPool(params)
  256 + if (res.code === 0) {
  257 + this.$message.success(this.$t('common.submitSuccess'))
  258 + this.goBack()
  259 + } else {
  260 + this.$message.error(res.msg)
  261 + }
  262 + } catch (error) {
  263 + console.error('保存工作单失败:', error)
  264 + this.$message.error(this.$t('common.submitFailed'))
  265 + }
  266 + },
  267 + async _listWorkTypes() {
  268 + try {
  269 + const params = {
  270 + page: 1,
  271 + row: 100,
  272 + communityId: this.addWorkInfo.communityId
  273 + }
  274 + const res = await listWorkType(params)
  275 + this.addWorkInfo.workTypes = res.data
  276 + } catch (error) {
  277 + console.error('获取工作单类型失败:', error)
  278 + }
  279 + },
  280 + _chooseWorkStaff() {
  281 + this.chooseStaffFlag = '1'
  282 + this.$refs.selectStaff.open({
  283 + call: () => {
  284 + }
  285 + })
  286 + },
  287 + selectStaff(staff) {
  288 + if(this.chooseStaffFlag === '1'){
  289 + this.addWorkInfo.staffs.push({
  290 + staffId: staff.userId,
  291 + staffName: staff.userName
  292 + })
  293 + }else{
  294 + this.addWorkInfo.copyStaffs.push({
  295 + staffId: staff.userId,
  296 + staffName: staff.userName
  297 + })
  298 + }
  299 + },
  300 + _deleteWorkStaff(staff) {
  301 + this.addWorkInfo.staffs = this.addWorkInfo.staffs.filter(item => item.staffId !== staff.staffId)
  302 + },
  303 + _chooseCopyWorkStaff() {
  304 + this.chooseStaffFlag = '2'
  305 + this.$refs.selectStaff.open({
  306 + call: () => {
  307 + }
  308 + })
  309 + },
  310 + _deleteCopyWorkStaff(staff) {
  311 + this.addWorkInfo.copyStaffs = this.addWorkInfo.copyStaffs.filter(item => item.staffId !== staff.staffId)
  312 + },
  313 + addWorkContent() {
  314 + this.addWorkInfo.contents.push({
  315 + id: getUuid(),
  316 + content: ''
  317 + })
  318 + },
  319 + deleteWorkContent(content) {
  320 + this.addWorkInfo.contents = this.addWorkInfo.contents.filter(item => item.id !== content.id)
  321 + },
  322 + handleWorkCycleChange(val) {
  323 + if (val === '1001') {
  324 + this.addWorkInfo.period = ''
  325 + this.addWorkInfo.months = []
  326 + this.addWorkInfo.days = []
  327 + this.addWorkInfo.workdays = []
  328 + }
  329 + },
  330 + notifyFile(data) {
  331 + this.addWorkInfo.pathUrls.push(data.realFileName)
  332 + },
  333 + goBack() {
  334 + this.$router.go(-1)
  335 + }
  336 + }
  337 +}
  338 +</script>
  339 +
  340 +<style lang="scss" scoped>
  341 +.add-work-container {
  342 + padding: 20px;
  343 +
  344 + .staff-tags {
  345 + display: flex;
  346 + flex-wrap: wrap;
  347 + align-items: center;
  348 +
  349 + .el-tag {
  350 + margin-right: 10px;
  351 + margin-bottom: 5px;
  352 + }
  353 + }
  354 +
  355 + .flex {
  356 + display: flex;
  357 + }
  358 +
  359 + .align-center {
  360 + align-items: center;
  361 + }
  362 +
  363 + .text-right {
  364 + text-align: right;
  365 + }
  366 +}
  367 +</style>
0 \ No newline at end of file 368 \ No newline at end of file
src/views/oa/editWorkLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + editWork: {
  4 + title: 'Edit Work Order',
  5 + workName: 'Title',
  6 + workNamePlaceholder: 'Required, please enter title',
  7 + workType: 'Work Order Type',
  8 + workTypePlaceholder: 'Required, please select work order type',
  9 + processor: 'Processor',
  10 + ccPerson: 'CC Person',
  11 + choose: 'Choose',
  12 + workSign: 'Work Order Sign',
  13 + workSignPlaceholder: 'Required, please select work order sign',
  14 + onceWork: 'One-time Work Order',
  15 + cycleWork: 'Periodic Work Order',
  16 + attachment: 'Attachment',
  17 + startTime: 'Start Time',
  18 + startTimePlaceholder: 'Required, please enter start time',
  19 + endTime: 'End Time',
  20 + endTimePlaceholder: 'Required, please enter end time',
  21 + period: 'Period',
  22 + periodPlaceholder: 'Required, please select task period',
  23 + monthDay: 'Month/Day',
  24 + byWeek: 'By Week',
  25 + workHours: 'Work Hours',
  26 + workHoursPlaceholder: 'Required, please enter hours',
  27 + hoursComplete: 'hours to complete',
  28 + month: 'Month',
  29 + monthUnit: 'Month',
  30 + day: 'Day',
  31 + dayUnit: 'Day',
  32 + week: 'Week',
  33 + monday: 'Monday',
  34 + tuesday: 'Tuesday',
  35 + wednesday: 'Wednesday',
  36 + thursday: 'Thursday',
  37 + friday: 'Friday',
  38 + saturday: 'Saturday',
  39 + sunday: 'Sunday',
  40 + content: 'Content',
  41 + add: 'Add',
  42 + delete: 'Delete'
  43 + },
  44 + uploadFile: {
  45 + upload: 'Upload Attachment',
  46 + sizeLimit: 'File size cannot exceed 20MB',
  47 + success: 'File uploaded successfully',
  48 + failed: 'File upload failed'
  49 + },
  50 + textarea: {
  51 + placeholder: 'Required, please enter content',
  52 + uploadFailed: 'Image upload failed'
  53 + },
  54 + selectStaff: {
  55 + title: 'Select Staff',
  56 + orgInfo: 'Organization Information',
  57 + staffInfo: 'Staff Information',
  58 + submitter: 'Submitter',
  59 + dynamicAssign: 'Dynamic Assign'
  60 + }
  61 + },
  62 + zh: {
  63 + editWork: {
  64 + title: '修改工作单',
  65 + workName: '标题',
  66 + workNamePlaceholder: '必填,请填写标题',
  67 + workType: '工作单类型',
  68 + workTypePlaceholder: '必填,请选择工作单类型',
  69 + processor: '处理人',
  70 + ccPerson: '抄送人',
  71 + choose: '选择',
  72 + workSign: '工作单标识',
  73 + workSignPlaceholder: '必填,请选择工作单标识',
  74 + onceWork: '一次性工单',
  75 + cycleWork: '周期性工单',
  76 + attachment: '附件',
  77 + startTime: '开始时间',
  78 + startTimePlaceholder: '必填,请填写开始时间',
  79 + endTime: '结束时间',
  80 + endTimePlaceholder: '必填,请填写结束时间',
  81 + period: '周期',
  82 + periodPlaceholder: '必填,请选择任务周期',
  83 + monthDay: '月/天',
  84 + byWeek: '按周',
  85 + workHours: '工作单',
  86 + workHoursPlaceholder: '必填,请输入小时数',
  87 + hoursComplete: '小时内完成',
  88 + month: '月',
  89 + monthUnit: '月',
  90 + day: '日',
  91 + dayUnit: '日',
  92 + week: '周',
  93 + monday: '星期一',
  94 + tuesday: '星期二',
  95 + wednesday: '星期三',
  96 + thursday: '星期四',
  97 + friday: '星期五',
  98 + saturday: '星期六',
  99 + sunday: '星期日',
  100 + content: '内容',
  101 + add: '添加',
  102 + delete: '删除'
  103 + },
  104 + uploadFile: {
  105 + upload: '上传附件',
  106 + sizeLimit: '文件大小不能超过20MB',
  107 + success: '文件上传成功',
  108 + failed: '文件上传失败'
  109 + },
  110 + textarea: {
  111 + placeholder: '必填,请输入内容',
  112 + uploadFailed: '图片上传失败'
  113 + },
  114 + selectStaff: {
  115 + title: '选择员工',
  116 + orgInfo: '组织信息',
  117 + staffInfo: '员工信息',
  118 + submitter: '提交者',
  119 + dynamicAssign: '动态指定'
  120 + }
  121 + }
  122 +}
0 \ No newline at end of file 123 \ No newline at end of file
src/views/oa/editWorkList.vue 0 → 100644
  1 +<template>
  2 + <div class="edit-work-container">
  3 + <el-card class="box-card">
  4 + <div slot="header" class="clearfix">
  5 + <span>{{ $t('editWork.title') }}</span>
  6 + </div>
  7 +
  8 + <el-form ref="form" :model="editWorkInfo" label-width="120px" label-position="right">
  9 + <el-row :gutter="20">
  10 + <el-col :span="12">
  11 + <el-form-item :label="$t('editWork.workName')" prop="workName">
  12 + <el-input v-model.trim="editWorkInfo.workName" :placeholder="$t('editWork.workNamePlaceholder')"
  13 + maxlength="20" clearable />
  14 + </el-form-item>
  15 + </el-col>
  16 + <el-col :span="12">
  17 + <el-form-item :label="$t('editWork.workType')" prop="wtId">
  18 + <el-select v-model="editWorkInfo.wtId" :placeholder="$t('editWork.workTypePlaceholder')" style="width:100%"
  19 + clearable>
  20 + <el-option v-for="item in editWorkInfo.workTypes" :key="item.wtId" :label="item.typeName"
  21 + :value="item.wtId" />
  22 + </el-select>
  23 + </el-form-item>
  24 + </el-col>
  25 + </el-row>
  26 +
  27 + <el-row :gutter="20">
  28 + <el-col :span="12">
  29 + <el-form-item :label="$t('editWork.processor')">
  30 + <div class="staff-tags">
  31 + <el-tag v-for="(staff, index) in editWorkInfo.staffs" :key="index" closable
  32 + @close="_deleteWorkStaff(staff)">
  33 + {{ staff.staffName }}
  34 + </el-tag>
  35 + <el-button type="text" @click="_chooseWorkStaff">
  36 + <i class="el-icon-search"></i>{{ $t('editWork.choose') }}
  37 + </el-button>
  38 + </div>
  39 + </el-form-item>
  40 + </el-col>
  41 + <el-col :span="12">
  42 + <el-form-item :label="$t('editWork.ccPerson')">
  43 + <div class="staff-tags">
  44 + <el-tag v-for="(staff, index) in editWorkInfo.copyStaffs" :key="index" closable
  45 + @close="_deleteCopyWorkStaff(staff)">
  46 + {{ staff.staffName }}
  47 + </el-tag>
  48 + <el-button type="text" @click="_chooseCopyWorkStaff">
  49 + <i class="el-icon-search"></i>{{ $t('editWork.choose') }}
  50 + </el-button>
  51 + </div>
  52 + </el-form-item>
  53 + </el-col>
  54 + </el-row>
  55 +
  56 + <el-row :gutter="20">
  57 + <el-col :span="12">
  58 + <el-form-item :label="$t('editWork.workSign')" prop="workCycle">
  59 + <el-select v-model="editWorkInfo.workCycle" :placeholder="$t('editWork.workSignPlaceholder')"
  60 + style="width:100%" @change="handleWorkCycleChange">
  61 + <el-option :label="$t('editWork.onceWork')" value="1001" />
  62 + <el-option :label="$t('editWork.cycleWork')" value="2002" />
  63 + </el-select>
  64 + </el-form-item>
  65 + </el-col>
  66 + <el-col :span="12">
  67 + <el-form-item :label="$t('editWork.attachment')">
  68 + <upload-file ref="uploadFile" @notifyFile="notifyFile" :defaultFiles="editWorkInfo.pathUrls" />
  69 + </el-form-item>
  70 + </el-col>
  71 + </el-row>
  72 +
  73 + <el-row :gutter="20">
  74 + <el-col :span="12">
  75 + <el-form-item :label="$t('editWork.startTime')" prop="startTime">
  76 + <el-date-picker v-model="editWorkInfo.startTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
  77 + :placeholder="$t('editWork.startTimePlaceholder')" style="width:100%" />
  78 + </el-form-item>
  79 + </el-col>
  80 + <el-col :span="12">
  81 + <el-form-item :label="$t('editWork.endTime')" prop="endTime">
  82 + <el-date-picker v-model="editWorkInfo.endTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
  83 + :placeholder="$t('editWork.endTimePlaceholder')" style="width:100%" />
  84 + </el-form-item>
  85 + </el-col>
  86 + </el-row>
  87 +
  88 + <template v-if="editWorkInfo.workCycle === '2002'">
  89 + <el-row :gutter="20">
  90 + <el-col :span="12">
  91 + <el-form-item :label="$t('editWork.period')" prop="period">
  92 + <el-select v-model="editWorkInfo.period" :placeholder="$t('editWork.periodPlaceholder')"
  93 + style="width:100%">
  94 + <el-option :label="$t('editWork.monthDay')" value="2020022" />
  95 + <el-option :label="$t('editWork.byWeek')" value="2020023" />
  96 + </el-select>
  97 + </el-form-item>
  98 + </el-col>
  99 + <el-col :span="12">
  100 + <el-form-item :label="$t('editWork.workHours')">
  101 + <el-input v-model.trim="editWorkInfo.hours" :placeholder="$t('editWork.workHoursPlaceholder')">
  102 + <template slot="append">{{ $t('editWork.hoursComplete') }}</template>
  103 + </el-input>
  104 + </el-form-item>
  105 + </el-col>
  106 + </el-row>
  107 +
  108 + <template v-if="editWorkInfo.period === '2020022'">
  109 + <el-row :gutter="20">
  110 + <el-col :span="24">
  111 + <el-form-item :label="$t('editWork.month')">
  112 + <el-checkbox-group v-model="editWorkInfo.months">
  113 + <el-checkbox v-for="index in 12" :key="index" :label="index">
  114 + {{ index }}{{ $t('editWork.monthUnit') }}
  115 + </el-checkbox>
  116 + </el-checkbox-group>
  117 + </el-form-item>
  118 + </el-col>
  119 + </el-row>
  120 +
  121 + <el-row :gutter="20">
  122 + <el-col :span="24">
  123 + <el-form-item :label="$t('editWork.day')">
  124 + <el-checkbox-group v-model="editWorkInfo.days">
  125 + <el-checkbox v-for="index in 31" :key="index" :label="index">
  126 + {{ index }}{{ $t('editWork.dayUnit') }}
  127 + </el-checkbox>
  128 + </el-checkbox-group>
  129 + </el-form-item>
  130 + </el-col>
  131 + </el-row>
  132 + </template>
  133 +
  134 + <template v-if="editWorkInfo.period === '2020023'">
  135 + <el-row :gutter="20">
  136 + <el-col :span="24">
  137 + <el-form-item :label="$t('editWork.week')">
  138 + <el-checkbox-group v-model="editWorkInfo.workdays">
  139 + <el-checkbox v-for="(day, index) in weekDays" :key="index" :label="index + 1">
  140 + {{ day }}
  141 + </el-checkbox>
  142 + </el-checkbox-group>
  143 + </el-form-item>
  144 + </el-col>
  145 + </el-row>
  146 + </template>
  147 + </template>
  148 +
  149 + <el-row v-for="(item, index) in editWorkInfo.contents" :key="item.id" :gutter="20">
  150 + <el-col :span="18">
  151 + <el-form-item :label="`${$t('editWork.content')}${index + 1}`">
  152 + <el-input v-model="item.content" type="textarea" :rows="5"
  153 + :placeholder="$t('editWork.contentPlaceholder')" />
  154 + </el-form-item>
  155 + </el-col>
  156 + <el-col :span="6" class="flex align-center">
  157 + <el-button v-if="index === 0" type="text" @click="addWorkContent">
  158 + {{ $t('editWork.add') }}
  159 + </el-button>
  160 + <el-button v-else type="text" @click="deleteWorkContent(item)">
  161 + {{ $t('editWork.delete') }}
  162 + </el-button>
  163 + </el-col>
  164 + </el-row>
  165 +
  166 + <el-row>
  167 + <el-col :span="24" class="text-right">
  168 + <el-button type="warning" @click="goBack">
  169 + <i class="el-icon-close"></i>{{ $t('common.back') }}
  170 + </el-button>
  171 + <el-button type="primary" @click="saveWorkPool">
  172 + <i class="el-icon-check"></i>{{ $t('common.submit') }}
  173 + </el-button>
  174 + </el-col>
  175 + </el-row>
  176 + </el-form>
  177 + </el-card>
  178 +
  179 + <select-staff ref="selectStaff" @selectStaff="selectStaff" />
  180 + </div>
  181 +</template>
  182 +
  183 +<script>
  184 +import { updateWorkPool, listWorkType, queryStartWork, listWorkTask, listWorkCopy, listWorkCycle } from '@/api/oa/editWorkApi'
  185 +import { getCommunityId } from '@/api/community/communityApi'
  186 +import SelectStaff from '@/components/staff/SelectStaff'
  187 +import UploadFile from '@/components/upload/uploadFile'
  188 +import { getUuid } from '@/utils/commonUtil'
  189 +import { dateFormat } from '@/utils/dateUtil'
  190 +
  191 +export default {
  192 + name: 'EditWorkList',
  193 + components: {
  194 + SelectStaff,
  195 + UploadFile
  196 + },
  197 + data() {
  198 + return {
  199 + chooseStaffFlag: '1',
  200 + editWorkInfo: {
  201 + workId: '',
  202 + workName: '',
  203 + workTypes: [],
  204 + wtId: '',
  205 + workCycle: '1001',
  206 + startTime: '',
  207 + endTime: '',
  208 + staffs: [],
  209 + copyStaffs: [],
  210 + pathUrls: [],
  211 + contents: [],
  212 + period: '',
  213 + months: [],
  214 + days: [],
  215 + workdays: [],
  216 + hours: '24',
  217 + communityId: ''
  218 + },
  219 + weekDays: [
  220 + this.$t('editWork.monday'),
  221 + this.$t('editWork.tuesday'),
  222 + this.$t('editWork.wednesday'),
  223 + this.$t('editWork.thursday'),
  224 + this.$t('editWork.friday'),
  225 + this.$t('editWork.saturday'),
  226 + this.$t('editWork.sunday')
  227 + ]
  228 + }
  229 + },
  230 + created() {
  231 + this.editWorkInfo.workId = this.$route.query.workId
  232 + this.editWorkInfo.communityId = getCommunityId()
  233 + this.loadData()
  234 + },
  235 + methods: {
  236 + async loadData() {
  237 + try {
  238 + await this._loadWorkPool()
  239 + await this._loadTaskStaff()
  240 + await this._loadWorkCopy()
  241 + await this._listWorkTypes()
  242 + await this._loadWorkCycle()
  243 + } catch (error) {
  244 + console.error('加载数据失败:', error)
  245 + this.$message.error(this.$t('common.loadFailed'))
  246 + }
  247 + },
  248 + editWorkValidate() {
  249 + return this.$refs.form.validate()
  250 + },
  251 + async saveWorkPool() {
  252 + try {
  253 + const valid = await this.editWorkValidate()
  254 + if (!valid) return
  255 +
  256 + const params = {
  257 + ...this.editWorkInfo,
  258 + // 处理月份和日期数组为字符串
  259 + months: this.editWorkInfo.months.join(','),
  260 + days: this.editWorkInfo.days.join(','),
  261 + workdays: this.editWorkInfo.workdays.join(',')
  262 + }
  263 +
  264 + const res = await updateWorkPool(params)
  265 + if (res.code === 0) {
  266 + this.$message.success(this.$t('common.submitSuccess'))
  267 + this.goBack()
  268 + } else {
  269 + this.$message.error(res.msg)
  270 + }
  271 + } catch (error) {
  272 + console.error('保存工作单失败:', error)
  273 + this.$message.error(this.$t('common.submitFailed'))
  274 + }
  275 + },
  276 + async _listWorkTypes() {
  277 + try {
  278 + const params = {
  279 + page: 1,
  280 + row: 100,
  281 + communityId: this.editWorkInfo.communityId
  282 + }
  283 + const res = await listWorkType(params)
  284 + this.editWorkInfo.workTypes = res.data
  285 + } catch (error) {
  286 + console.error('获取工作单类型失败:', error)
  287 + }
  288 + },
  289 + _chooseWorkStaff() {
  290 + this.chooseStaffFlag = '1'
  291 + this.$refs.selectStaff.open({
  292 + call: () => {
  293 + }
  294 + })
  295 + },
  296 + selectStaff(staff) {
  297 + if (this.chooseStaffFlag === '1') {
  298 + this.editWorkInfo.staffs.push({
  299 + staffId: staff.userId,
  300 + staffName: staff.userName
  301 + })
  302 + } else {
  303 + this.editWorkInfo.copyStaffs.push({
  304 + staffId: staff.userId,
  305 + staffName: staff.userName
  306 + })
  307 + }
  308 + },
  309 + _deleteWorkStaff(staff) {
  310 + this.editWorkInfo.staffs = this.editWorkInfo.staffs.filter(item => item.staffId !== staff.staffId)
  311 + },
  312 + _chooseCopyWorkStaff() {
  313 + this.chooseStaffFlag = '2'
  314 + this.$refs.selectStaff.open({
  315 + call: () => {
  316 + }
  317 + })
  318 + },
  319 + _deleteCopyWorkStaff(staff) {
  320 + this.editWorkInfo.copyStaffs = this.editWorkInfo.copyStaffs.filter(item => item.staffId !== staff.staffId)
  321 + },
  322 + async _loadWorkPool() {
  323 + const params = {
  324 + page: 1,
  325 + row: 1,
  326 + workId: this.editWorkInfo.workId
  327 + }
  328 + const { data } = await queryStartWork(params)
  329 + if (data && data.length > 0) {
  330 + Object.assign(this.editWorkInfo, data[0])
  331 + // 格式化日期
  332 + if (data[0].startTime) {
  333 + this.editWorkInfo.startTime = dateFormat(new Date(data[0].startTime))
  334 + }
  335 + if (data[0].endTime) {
  336 + this.editWorkInfo.endTime = dateFormat(new Date(data[0].endTime))
  337 + }
  338 + // 处理内容
  339 + this.editWorkInfo.contents = data[0].contents.map(c => ({
  340 + id: getUuid(),
  341 + content: c.content
  342 + })) || [{
  343 + id: getUuid(),
  344 + content: ''
  345 + }]
  346 + }
  347 + },
  348 + async _loadTaskStaff() {
  349 + const params = {
  350 + page: 1,
  351 + row: 100,
  352 + workId: this.editWorkInfo.workId
  353 + }
  354 + const { data } = await listWorkTask(params)
  355 + this.editWorkInfo.staffs = data || []
  356 + },
  357 + async _loadWorkCopy() {
  358 + const params = {
  359 + page: 1,
  360 + row: 100,
  361 + workId: this.editWorkInfo.workId
  362 + }
  363 + const { data } = await listWorkCopy(params)
  364 + this.editWorkInfo.copyStaffs = data || []
  365 + },
  366 + async _loadWorkCycle() {
  367 + const params = {
  368 + page: 1,
  369 + row: 1,
  370 + workId: this.editWorkInfo.workId
  371 + }
  372 + const { data } = await listWorkCycle(params)
  373 + if (data && data.length > 0) {
  374 + this.editWorkInfo.period = data[0].period
  375 + this.editWorkInfo.workCycle = '2002' // 设置为周期工作
  376 + this.editWorkInfo.hours = data[0].hours || '24'
  377 +
  378 + // 处理月份、日期和工作日
  379 + if (data[0].periodMonth) {
  380 + this.editWorkInfo.months = data[0].periodMonth.split(',').map(Number)
  381 + }
  382 + if (data[0].periodDay) {
  383 + this.editWorkInfo.days = data[0].periodDay.split(',').map(Number)
  384 + }
  385 + if (data[0].workdays) {
  386 + this.editWorkInfo.workdays = data[0].workdays.split(',').map(Number)
  387 + }
  388 + }
  389 + },
  390 + addWorkContent() {
  391 + this.editWorkInfo.contents.push({
  392 + id: getUuid(),
  393 + content: ''
  394 + })
  395 + },
  396 + deleteWorkContent(content) {
  397 + this.editWorkInfo.contents = this.editWorkInfo.contents.filter(item => item.id !== content.id)
  398 + },
  399 + handleWorkCycleChange(val) {
  400 + if (val === '1001') {
  401 + this.editWorkInfo.period = ''
  402 + this.editWorkInfo.months = []
  403 + this.editWorkInfo.days = []
  404 + this.editWorkInfo.workdays = []
  405 + }
  406 + },
  407 + notifyFile(data) {
  408 + this.editWorkInfo.pathUrls.push(data.realFileName)
  409 + },
  410 + goBack() {
  411 + this.$router.go(-1)
  412 + }
  413 + }
  414 +}
  415 +</script>
  416 +
  417 +<style lang="scss" scoped>
  418 +.edit-work-container {
  419 + padding: 20px;
  420 +
  421 + .staff-tags {
  422 + display: flex;
  423 + flex-wrap: wrap;
  424 + align-items: center;
  425 +
  426 + .el-tag {
  427 + margin-right: 10px;
  428 + margin-bottom: 5px;
  429 + }
  430 + }
  431 +
  432 + .flex {
  433 + display: flex;
  434 + }
  435 +
  436 + .align-center {
  437 + align-items: center;
  438 + }
  439 +
  440 + .text-right {
  441 + text-align: right;
  442 + }
  443 +
  444 + .el-checkbox-group {
  445 + display: flex;
  446 + flex-wrap: wrap;
  447 + gap: 10px;
  448 + }
  449 +}
  450 +</style>
0 \ No newline at end of file 451 \ No newline at end of file
src/views/oa/startWorkLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + startWork: {
  4 + state: {
  5 + all: 'All',
  6 + W: 'Pending',
  7 + D: 'Processing',
  8 + C: 'Completed'
  9 + },
  10 + search: {
  11 + title: 'Search Conditions',
  12 + workName: 'Work Order Name',
  13 + staffName: 'Handler',
  14 + startTime: 'Start Time',
  15 + endTime: 'End Time'
  16 + },
  17 + list: {
  18 + title: 'Work Order List',
  19 + draft: 'Draft'
  20 + },
  21 + table: {
  22 + workId: 'ID',
  23 + workName: 'Work Order Name',
  24 + typeName: 'Type Name',
  25 + workCycle: 'Cycle',
  26 + startTime: 'Start Time',
  27 + endTime: 'End Time',
  28 + createUser: 'Creator',
  29 + handler: 'Handler',
  30 + ccUser: 'CC User',
  31 + state: 'Status',
  32 + createTime: 'Create Time'
  33 + },
  34 + cycle: {
  35 + once: 'One-time',
  36 + periodic: 'Periodic'
  37 + },
  38 + operation: {
  39 + stop: 'Stop',
  40 + start: 'Start'
  41 + },
  42 + delete: {
  43 + title: 'Confirm Operation',
  44 + confirm: 'Are you sure to delete this work order?',
  45 + success: 'Delete successfully',
  46 + error: 'Delete failed'
  47 + }
  48 + },
  49 + },
  50 + zh: {
  51 + startWork: {
  52 + state: {
  53 + all: '全部',
  54 + W: '待处理',
  55 + D: '处理中',
  56 + C: '处理完成'
  57 + },
  58 + search: {
  59 + title: '查询条件',
  60 + workName: '工单名称',
  61 + staffName: '处理人',
  62 + startTime: '开始时间',
  63 + endTime: '结束时间'
  64 + },
  65 + list: {
  66 + title: '发起工作单',
  67 + draft: '起草'
  68 + },
  69 + table: {
  70 + workId: '编号',
  71 + workName: '工单名称',
  72 + typeName: '类型名称',
  73 + workCycle: '标识',
  74 + startTime: '开始时间',
  75 + endTime: '结束时间',
  76 + createUser: '发起人',
  77 + handler: '处理人',
  78 + ccUser: '抄送人',
  79 + state: '状态',
  80 + createTime: '创建时间'
  81 + },
  82 + cycle: {
  83 + once: '一次性工单',
  84 + periodic: '周期性工单'
  85 + },
  86 + operation: {
  87 + stop: '停止',
  88 + start: '启用'
  89 + },
  90 + delete: {
  91 + title: '请确认您的操作',
  92 + confirm: '确定删除工作单',
  93 + success: '删除成功',
  94 + error: '删除失败'
  95 + }
  96 + }
  97 + }
  98 +}
0 \ No newline at end of file 99 \ No newline at end of file
src/views/oa/startWorkList.vue 0 → 100644
  1 +<template>
  2 + <div class="start-work-container">
  3 + <el-row :gutter="20">
  4 + <el-col :span="2">
  5 + <div class="state-sidebar">
  6 + <ul class="state-list">
  7 + <li v-for="(item, index) in states" :key="index" class="state-item"
  8 + :class="{ 'active-state': conditions.state === item.state }" @click="switchWorkState(item)">
  9 + {{ $t(`startWork.state.${item.state || 'all'}`) }}
  10 + </li>
  11 + </ul>
  12 + </div>
  13 + </el-col>
  14 + <el-col :span="22">
  15 + <el-card class="search-card">
  16 + <div slot="header" class="flex justify-between">
  17 + <span>{{ $t('startWork.search.title') }}</span>
  18 + </div>
  19 + <el-form :inline="true" :model="conditions" class="search-form">
  20 + <el-form-item>
  21 + <el-input v-model="conditions.workName" :placeholder="$t('startWork.search.workName')" clearable />
  22 + </el-form-item>
  23 + <el-form-item>
  24 + <el-input v-model="conditions.staffName" :placeholder="$t('startWork.search.staffName')" clearable />
  25 + </el-form-item>
  26 + <el-form-item>
  27 + <el-date-picker v-model="conditions.queryStartTime" type="datetime"
  28 + :placeholder="$t('startWork.search.startTime')" value-format="yyyy-MM-dd HH:mm:ss" />
  29 + </el-form-item>
  30 + <el-form-item>
  31 + <el-date-picker v-model="conditions.queryEndTime" type="datetime"
  32 + :placeholder="$t('startWork.search.endTime')" value-format="yyyy-MM-dd HH:mm:ss"
  33 + :picker-options="endDateOptions" />
  34 + </el-form-item>
  35 + <el-form-item>
  36 + <el-button type="primary" @click="handleSearch">
  37 + {{ $t('common.search') }}
  38 + </el-button>
  39 + <el-button @click="handleReset">
  40 + {{ $t('common.reset') }}
  41 + </el-button>
  42 + </el-form-item>
  43 + </el-form>
  44 + </el-card>
  45 +
  46 + <el-card class="work-list-card">
  47 + <div slot="header" class="flex justify-between">
  48 + <span>{{ $t('startWork.list.title') }}</span>
  49 + <el-button type="primary" size="small" style="float: right" @click="handleAdd">
  50 + <i class="el-icon-plus" />
  51 + {{ $t('startWork.list.draft') }}
  52 + </el-button>
  53 + </div>
  54 + <el-table v-loading="loading" :data="works" border style="width: 100%">
  55 + <el-table-column prop="workId" :label="$t('startWork.table.workId')" align="center" />
  56 + <el-table-column prop="workName" :label="$t('startWork.table.workName')" align="center" />
  57 + <el-table-column prop="typeName" :label="$t('startWork.table.typeName')" align="center" />
  58 + <el-table-column :label="$t('startWork.table.workCycle')" align="center">
  59 + <template slot-scope="scope">
  60 + {{ scope.row.workCycle === '1001' ? $t('startWork.cycle.once') : $t('startWork.cycle.periodic') }}
  61 + </template>
  62 + </el-table-column>
  63 + <el-table-column prop="startTime" :label="$t('startWork.table.startTime')" align="center" />
  64 + <el-table-column prop="endTime" :label="$t('startWork.table.endTime')" align="center" />
  65 + <el-table-column prop="createUserName" :label="$t('startWork.table.createUser')" align="center" />
  66 + <el-table-column prop="curStaffName" :label="$t('startWork.table.handler')" align="center">
  67 + <template slot-scope="scope">
  68 + {{ scope.row.curStaffName || '-' }}
  69 + </template>
  70 + </el-table-column>
  71 + <el-table-column prop="curCopyName" :label="$t('startWork.table.ccUser')" align="center">
  72 + <template slot-scope="scope">
  73 + {{ scope.row.curCopyName || '-' }}
  74 + </template>
  75 + </el-table-column>
  76 + <el-table-column prop="stateName" :label="$t('startWork.table.state')" align="center" />
  77 + <el-table-column prop="createTime" :label="$t('startWork.table.createTime')" align="center" />
  78 + <el-table-column :label="$t('common.operation')" align="center" width="300">
  79 + <template slot-scope="scope">
  80 + <el-button size="mini" @click="handleDetail(scope.row)">
  81 + {{ $t('common.detail') }}
  82 + </el-button>
  83 + <el-button v-if="scope.row.state === 'W'" size="mini" type="primary" @click="handleEdit(scope.row)">
  84 + {{ $t('common.edit') }}
  85 + </el-button>
  86 + <el-button size="mini" type="danger" @click="handleDelete(scope.row)">
  87 + {{ $t('common.delete') }}
  88 + </el-button>
  89 + <el-button v-if="scope.row.state !== 'S'" size="mini" type="warning"
  90 + @click="handleUpdateState(scope.row, 'S')">
  91 + {{ $t('startWork.operation.stop') }}
  92 + </el-button>
  93 + <el-button v-if="scope.row.state === 'S'" size="mini" type="success"
  94 + @click="handleUpdateState(scope.row, 'W')">
  95 + {{ $t('startWork.operation.start') }}
  96 + </el-button>
  97 + </template>
  98 + </el-table-column>
  99 + </el-table>
  100 + <el-pagination :current-page="page.current" :page-sizes="[10, 20, 30, 50]" :page-size="page.size"
  101 + :total="page.total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange"
  102 + @current-change="handlePageChange" />
  103 + </el-card>
  104 + </el-col>
  105 + </el-row>
  106 +
  107 + <delete-work ref="deleteWork" @success="fetchData" />
  108 + </div>
  109 +</template>
  110 +
  111 +<script>
  112 +import { queryStartWork, updateWorkState } from '@/api/oa/startWorkApi'
  113 +import DeleteWork from '@/components/oa/deleteWork'
  114 +import { getCommunityId } from '@/api/community/communityApi'
  115 +
  116 +export default {
  117 + name: 'StartWorkList',
  118 + components: {
  119 + DeleteWork
  120 + },
  121 + data() {
  122 + return {
  123 + loading: false,
  124 + communityId: '',
  125 + states: [
  126 + { name: '全部', state: '' },
  127 + { name: '待处理', state: 'W' },
  128 + { name: '处理中', state: 'D' },
  129 + { name: '处理完成', state: 'C' }
  130 + ],
  131 + works: [],
  132 + conditions: {
  133 + state: '',
  134 + workName: '',
  135 + staffName: '',
  136 + queryStartTime: '',
  137 + queryEndTime: ''
  138 + },
  139 + page: {
  140 + current: 1,
  141 + size: 10,
  142 + total: 0
  143 + },
  144 + endDateOptions: {
  145 + disabledDate: (time) => {
  146 + if (this.conditions.queryStartTime) {
  147 + return time.getTime() <= new Date(this.conditions.queryStartTime).getTime()
  148 + }
  149 + return false
  150 + }
  151 + }
  152 + }
  153 + },
  154 + created() {
  155 + this.communityId = getCommunityId()
  156 + this.fetchData()
  157 + },
  158 + methods: {
  159 + async fetchData() {
  160 + this.loading = true
  161 + try {
  162 + const params = {
  163 + ...this.conditions,
  164 + page: this.page.current,
  165 + row: this.page.size,
  166 + communityId: this.communityId
  167 + }
  168 + const { data, total } = await queryStartWork(params)
  169 + this.works = data
  170 + this.page.total = total
  171 + } catch (error) {
  172 + console.error('获取工单列表失败:', error)
  173 + } finally {
  174 + this.loading = false
  175 + }
  176 + },
  177 + switchWorkState(item) {
  178 + this.conditions.state = item.state
  179 + this.page.current = 1
  180 + this.fetchData()
  181 + },
  182 + handleSearch() {
  183 + this.page.current = 1
  184 + this.fetchData()
  185 + },
  186 + handleReset() {
  187 + this.conditions = {
  188 + state: '',
  189 + workName: '',
  190 + staffName: '',
  191 + queryStartTime: '',
  192 + queryEndTime: ''
  193 + }
  194 + this.page.current = 1
  195 + this.fetchData()
  196 + },
  197 + handleAdd() {
  198 + this.$router.push('/views/oa/addWork')
  199 + },
  200 + handleEdit(row) {
  201 + this.$router.push(`/views/oa/editWork?workId=${row.workId}`)
  202 + },
  203 + handleDetail(row) {
  204 + this.$router.push(`/oa/workDetail?workId=${row.workId}`)
  205 + },
  206 + handleDelete(row) {
  207 + this.$refs.deleteWork.open(row)
  208 + },
  209 + async handleUpdateState(row, state) {
  210 + try {
  211 + await updateWorkState({
  212 + workId: row.workId,
  213 + state,
  214 + communityId: this.communityId
  215 + })
  216 + this.$message.success(state === 'S' ? '停止成功' : '启用成功')
  217 + this.fetchData()
  218 + } catch (error) {
  219 + console.error('更新状态失败:', error)
  220 + }
  221 + },
  222 + handleSizeChange(size) {
  223 + this.page.size = size
  224 + this.fetchData()
  225 + },
  226 + handlePageChange(current) {
  227 + this.page.current = current
  228 + this.fetchData()
  229 + }
  230 + }
  231 +}
  232 +</script>
  233 +
  234 +<style lang="scss" scoped>
  235 +.start-work-container {
  236 + padding: 20px;
  237 +
  238 + .state-sidebar {
  239 + background-color: #fff;
  240 + border-radius: 4px;
  241 + padding: 10px 0;
  242 + height: 100%;
  243 +
  244 + .state-list {
  245 + list-style: none;
  246 + padding: 0;
  247 + margin: 0;
  248 +
  249 + .state-item {
  250 + padding: 10px 15px;
  251 + cursor: pointer;
  252 + text-align: center;
  253 + transition: all 0.3s;
  254 +
  255 + &:hover {
  256 + background-color: #f5f7fa;
  257 + }
  258 +
  259 + &.active-state {
  260 + background-color: #409eff;
  261 + color: #fff;
  262 + }
  263 + }
  264 + }
  265 + }
  266 +
  267 + .search-card {
  268 + margin-bottom: 20px;
  269 +
  270 + .search-form {
  271 + .el-form-item {
  272 + margin-bottom: 0;
  273 + }
  274 + }
  275 + }
  276 +
  277 + .work-list-card {
  278 + .el-table {
  279 + margin-top: 10px;
  280 + }
  281 +
  282 + .el-pagination {
  283 + margin-top: 20px;
  284 + text-align: right;
  285 + }
  286 + }
  287 +}
  288 +</style>
0 \ No newline at end of file 289 \ No newline at end of file
src/views/oa/workTypeLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + workType: {
  4 + search: {
  5 + title: 'Search Conditions',
  6 + typeName: 'Type Name'
  7 + },
  8 + list: {
  9 + title: 'Work Order Types'
  10 + },
  11 + table: {
  12 + typeName: 'Type Name',
  13 + deduction: 'Deduction',
  14 + smsWay: 'Notification Method',
  15 + createTime: 'Create Time',
  16 + remark: 'Remark'
  17 + },
  18 + form: {
  19 + typeName: 'Type Name',
  20 + deduction: 'Deduction',
  21 + smsWay: 'Notification Method',
  22 + remark: 'Remark'
  23 + },
  24 + placeholder: {
  25 + typeName: 'Please enter type name',
  26 + deduction: 'Please select deduction',
  27 + smsWay: 'Please select notification method',
  28 + remark: 'Optional, please enter remark'
  29 + },
  30 + validate: {
  31 + typeName: 'Type name is required',
  32 + typeNameMax: 'Type name cannot exceed 200 characters',
  33 + deduction: 'Deduction is required',
  34 + smsWay: 'Notification method is required',
  35 + wtId: 'ID is required'
  36 + },
  37 + tip: {
  38 + deduction: 'After selecting deduction, the copy recipient will deduct money according to the task completion!',
  39 + wechat: 'Need to configure the official account in the system, the official account is a certified service account, and the employee has done employee certification',
  40 + aliSms: 'Need to configure Alibaba Cloud SMS information in the system community configuration, the template is work order pending template and work order completion template'
  41 + },
  42 + yes: 'Yes',
  43 + no: 'No',
  44 + wechat: 'WeChat',
  45 + aliSms: 'Ali SMS',
  46 + workLicense: 'Employee License',
  47 + unknown: 'Unknown',
  48 + add: {
  49 + title: 'Add Work Order Type',
  50 + success: 'Add success',
  51 + error: 'Add failed'
  52 + },
  53 + edit: {
  54 + title: 'Edit Work Order Type',
  55 + success: 'Edit success',
  56 + error: 'Edit failed'
  57 + },
  58 + delete: {
  59 + title: 'Delete Confirmation',
  60 + confirm: 'Are you sure to delete this work order type?',
  61 + success: 'Delete success',
  62 + error: 'Delete failed'
  63 + },
  64 + fetchError: 'Failed to fetch work order types'
  65 + }
  66 + },
  67 + zh: {
  68 + workType: {
  69 + search: {
  70 + title: '查询条件',
  71 + typeName: '类型名称'
  72 + },
  73 + list: {
  74 + title: '工作单类型'
  75 + },
  76 + table: {
  77 + typeName: '类型名称',
  78 + deduction: '扣款',
  79 + smsWay: '通知方式',
  80 + createTime: '创建时间',
  81 + remark: '备注'
  82 + },
  83 + form: {
  84 + typeName: '类型名称',
  85 + deduction: '扣款',
  86 + smsWay: '通知方式',
  87 + remark: '备注'
  88 + },
  89 + placeholder: {
  90 + typeName: '请输入类型名称',
  91 + deduction: '请选择扣款',
  92 + smsWay: '请选择通知方式',
  93 + remark: '选填,请输入备注'
  94 + },
  95 + validate: {
  96 + typeName: '类型名称不能为空',
  97 + typeNameMax: '类型名称不能超过200个字符',
  98 + deduction: '扣款不能为空',
  99 + smsWay: '通知方式不能为空',
  100 + wtId: '编号不能为空'
  101 + },
  102 + tip: {
  103 + deduction: '选择扣款后,抄送人会根据任务完成情况,相应的扣款!',
  104 + wechat: '需要在系统下公众号中配置公众号,公众号为认证过的服务号,并且员工做了员工认证',
  105 + aliSms: '需要在系统下小区配置中配置阿里云短信信息,模版为工单待处理模版和工单完成模版'
  106 + },
  107 + yes: '是',
  108 + no: '否',
  109 + wechat: '微信',
  110 + aliSms: '阿里短信',
  111 + workLicense: '员工工牌',
  112 + unknown: '未知',
  113 + add: {
  114 + title: '添加工作单类型',
  115 + success: '添加成功',
  116 + error: '添加失败'
  117 + },
  118 + edit: {
  119 + title: '修改工作单类型',
  120 + success: '修改成功',
  121 + error: '修改失败'
  122 + },
  123 + delete: {
  124 + title: '删除确认',
  125 + confirm: '确定删除此工作单类型吗?',
  126 + success: '删除成功',
  127 + error: '删除失败'
  128 + },
  129 + fetchError: '获取工作单类型失败'
  130 + }
  131 + }
  132 +}
0 \ No newline at end of file 133 \ No newline at end of file
src/views/oa/workTypeList.vue 0 → 100644
  1 +<template>
  2 + <div class="work-type-container">
  3 + <!-- 查询条件 -->
  4 + <el-card class="search-wrapper">
  5 + <div slot="header" class="flex justify-between">
  6 + <span>{{ $t('workType.search.title') }}</span>
  7 + </div>
  8 + <el-row :gutter="20">
  9 + <el-col :span="6">
  10 + <el-input v-model.trim="searchForm.typeName" :placeholder="$t('workType.search.typeName')" clearable />
  11 + </el-col>
  12 + <el-col :span="18" style="margin-top:5px;">
  13 + <el-button type="primary" @click="handleSearch">
  14 + <i class="el-icon-search"></i>
  15 + {{ $t('common.search') }}
  16 + </el-button>
  17 + <el-button @click="handleReset">
  18 + <i class="el-icon-refresh"></i>
  19 + {{ $t('common.reset') }}
  20 + </el-button>
  21 + </el-col>
  22 + </el-row>
  23 + </el-card>
  24 +
  25 + <!-- 工作单类型列表 -->
  26 + <el-card class="list-wrapper">
  27 + <div slot="header" class="flex justify-between">
  28 + <span>{{ $t('workType.list.title') }}</span>
  29 + <el-button type="primary" size="small" style="float:right;" @click="handleAdd">
  30 + <i class="el-icon-plus"></i>
  31 + {{ $t('common.add') }}
  32 + </el-button>
  33 + </div>
  34 +
  35 + <el-table v-loading="loading" :data="tableData" border style="width:100%">
  36 + <el-table-column prop="typeName" :label="$t('workType.table.typeName')" align="center" />
  37 + <el-table-column prop="deduction" :label="$t('workType.table.deduction')" align="center">
  38 + <template slot-scope="scope">
  39 + {{ scope.row.deduction === 'Y' ? $t('workType.yes') : $t('workType.no') }}
  40 + </template>
  41 + </el-table-column>
  42 + <el-table-column prop="smsWay" :label="$t('workType.table.smsWay')" align="center">
  43 + <template slot-scope="scope">
  44 + <span v-if="scope.row.smsWay === 'WECHAT'">{{ $t('workType.wechat') }}</span>
  45 + <span v-else-if="scope.row.smsWay === 'ALI_SMS'">{{ $t('workType.aliSms') }}</span>
  46 + <span v-else-if="scope.row.smsWay === 'WORK_LICENSE'">{{ $t('workType.workLicense') }}</span>
  47 + <span v-else>{{ $t('workType.unknown') }}</span>
  48 + </template>
  49 + </el-table-column>
  50 + <el-table-column prop="createTime" :label="$t('workType.table.createTime')" align="center" />
  51 + <el-table-column prop="remark" :label="$t('workType.table.remark')" align="center">
  52 + <template slot-scope="scope">
  53 + {{ scope.row.remark || '-' }}
  54 + </template>
  55 + </el-table-column>
  56 + <el-table-column :label="$t('common.operation')" align="center" width="200">
  57 + <template slot-scope="scope">
  58 + <el-button size="mini" type="primary" @click="handleEdit(scope.row)">
  59 + {{ $t('common.edit') }}
  60 + </el-button>
  61 + <el-button size="mini" type="danger" @click="handleDelete(scope.row)">
  62 + {{ $t('common.delete') }}
  63 + </el-button>
  64 + </template>
  65 + </el-table-column>
  66 + </el-table>
  67 +
  68 + <el-pagination :current-page.sync="page.current" :page-sizes="[10, 20, 30, 50]" :page-size="page.size"
  69 + :total="page.total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange"
  70 + @current-change="handleCurrentChange" />
  71 + </el-card>
  72 +
  73 + <!-- 组件 -->
  74 + <add-work-type ref="addWorkType" @success="handleSuccess" />
  75 + <edit-work-type ref="editWorkType" @success="handleSuccess" />
  76 + <delete-work-type ref="deleteWorkType" @success="handleSuccess" />
  77 + </div>
  78 +</template>
  79 +
  80 +<script>
  81 +import { listWorkType } from '@/api/oa/workTypeApi'
  82 +import { getCommunityId } from '@/api/community/communityApi'
  83 +import AddWorkType from '@/components/oa/addWorkType'
  84 +import EditWorkType from '@/components/oa/editWorkType'
  85 +import DeleteWorkType from '@/components/oa/deleteWorkType'
  86 +
  87 +export default {
  88 + name: 'WorkTypeList',
  89 + components: {
  90 + AddWorkType,
  91 + EditWorkType,
  92 + DeleteWorkType
  93 + },
  94 + data() {
  95 + return {
  96 + loading: false,
  97 + searchForm: {
  98 + typeName: ''
  99 + },
  100 + tableData: [],
  101 + page: {
  102 + current: 1,
  103 + size: 10,
  104 + total: 0
  105 + },
  106 + communityId: ''
  107 + }
  108 + },
  109 + created() {
  110 + this.communityId = getCommunityId()
  111 + this.getList()
  112 + },
  113 + methods: {
  114 + async getList() {
  115 + try {
  116 + this.loading = true
  117 + const params = {
  118 + page: this.page.current,
  119 + row: this.page.size,
  120 + typeName: this.searchForm.typeName,
  121 + communityId: this.communityId
  122 + }
  123 + const { data, total } = await listWorkType(params)
  124 + this.tableData = data
  125 + this.page.total = total
  126 + } catch (error) {
  127 + this.$message.error(this.$t('workType.fetchError'))
  128 + } finally {
  129 + this.loading = false
  130 + }
  131 + },
  132 + handleSearch() {
  133 + this.page.current = 1
  134 + this.getList()
  135 + },
  136 + handleReset() {
  137 + this.searchForm = {
  138 + typeName: ''
  139 + }
  140 + this.getList()
  141 + },
  142 + handleAdd() {
  143 + this.$refs.addWorkType.open()
  144 + },
  145 + handleEdit(row) {
  146 + this.$refs.editWorkType.open(row)
  147 + },
  148 + handleDelete(row) {
  149 + this.$refs.deleteWorkType.open(row)
  150 + },
  151 + handleSuccess() {
  152 + this.getList()
  153 + },
  154 + handleSizeChange(val) {
  155 + this.page.size = val
  156 + this.getList()
  157 + },
  158 + handleCurrentChange(val) {
  159 + this.page.current = val
  160 + this.getList()
  161 + }
  162 + }
  163 +}
  164 +</script>
  165 +
  166 +<style lang="scss" scoped>
  167 +.work-type-container {
  168 + padding: 20px;
  169 +
  170 + .search-wrapper {
  171 + margin-bottom: 20px;
  172 + }
  173 +
  174 + .list-wrapper {
  175 + margin-bottom: 20px;
  176 + }
  177 +
  178 + .el-pagination {
  179 + margin-top: 20px;
  180 + text-align: right;
  181 + }
  182 +}
  183 +</style>
0 \ No newline at end of file 184 \ No newline at end of file