Commit 0a06b2d19c364a091819054e1e044dbf07848536
1 parent
ec9ec2ce
开发完成投诉建议
Showing
22 changed files
with
2246 additions
and
141 deletions
src/api/oa/addComplaintApi.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 3 | + | |
| 4 | +// 获取投诉类型列表 | |
| 5 | +export function listComplaintTypes(params) { | |
| 6 | + return new Promise((resolve, reject) => { | |
| 7 | + const communityId = getCommunityId() | |
| 8 | + request({ | |
| 9 | + url: '/complaintType.listComplaintType', | |
| 10 | + method: 'get', | |
| 11 | + params: { | |
| 12 | + ...params, | |
| 13 | + communityId | |
| 14 | + } | |
| 15 | + }).then(response => { | |
| 16 | + const res = response.data | |
| 17 | + resolve(res) | |
| 18 | + }).catch(error => { | |
| 19 | + reject(error) | |
| 20 | + }) | |
| 21 | + }) | |
| 22 | +} | |
| 23 | + | |
| 24 | +// 保存投诉信息 | |
| 25 | +export function saveComplaint(data) { | |
| 26 | + return new Promise((resolve, reject) => { | |
| 27 | + const communityId = getCommunityId() | |
| 28 | + request({ | |
| 29 | + url: '/complaint.saveComplaint', | |
| 30 | + method: 'post', | |
| 31 | + data: { | |
| 32 | + ...data, | |
| 33 | + communityId | |
| 34 | + } | |
| 35 | + }).then(response => { | |
| 36 | + const res = response.data | |
| 37 | + if (res.code === 0) { | |
| 38 | + resolve(res) | |
| 39 | + } else { | |
| 40 | + reject(new Error(res.msg)) | |
| 41 | + } | |
| 42 | + }).catch(error => { | |
| 43 | + reject(error) | |
| 44 | + }) | |
| 45 | + }) | |
| 46 | +} | |
| 47 | + | |
| 48 | +// 查询房间信息 | |
| 49 | +export function queryRooms(params) { | |
| 50 | + return new Promise((resolve, reject) => { | |
| 51 | + const communityId = getCommunityId() | |
| 52 | + request({ | |
| 53 | + url: '/room.queryRooms', | |
| 54 | + method: 'get', | |
| 55 | + params: { | |
| 56 | + ...params, | |
| 57 | + communityId | |
| 58 | + } | |
| 59 | + }).then(response => { | |
| 60 | + const res = response.data | |
| 61 | + resolve(res) | |
| 62 | + }).catch(error => { | |
| 63 | + reject(error) | |
| 64 | + }) | |
| 65 | + }) | |
| 66 | +} | |
| 0 | 67 | \ No newline at end of file | ... | ... |
src/api/oa/addComplaintTypeApi.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * 获取组织树 | |
| 5 | + * @param {Object} params | |
| 6 | + * @returns {Promise} | |
| 7 | + */ | |
| 8 | +export function listOrgTree(params) { | |
| 9 | + return new Promise((resolve, reject) => { | |
| 10 | + request({ | |
| 11 | + url: '/org.listOrgTree', | |
| 12 | + method: 'get', | |
| 13 | + params | |
| 14 | + }).then(response => { | |
| 15 | + const res = response.data | |
| 16 | + resolve(res) | |
| 17 | + }).catch(error => { | |
| 18 | + reject(error) | |
| 19 | + }) | |
| 20 | + }) | |
| 21 | +} | |
| 22 | + | |
| 23 | +/** | |
| 24 | + * 查询员工信息 | |
| 25 | + * @param {Object} params | |
| 26 | + * @returns {Promise} | |
| 27 | + */ | |
| 28 | +export function getStaffInfos(params) { | |
| 29 | + return new Promise((resolve, reject) => { | |
| 30 | + request({ | |
| 31 | + url: '/query.staff.infos', | |
| 32 | + method: 'get', | |
| 33 | + params | |
| 34 | + }).then(response => { | |
| 35 | + const res = response.data | |
| 36 | + resolve(res) | |
| 37 | + }).catch(error => { | |
| 38 | + reject(error) | |
| 39 | + }) | |
| 40 | + }) | |
| 41 | +} | |
| 42 | + | |
| 43 | +/** | |
| 44 | + * 保存投诉类型 | |
| 45 | + * @param {Object} data | |
| 46 | + * @returns {Promise} | |
| 47 | + */ | |
| 48 | +export function saveComplaintType(data) { | |
| 49 | + return new Promise((resolve, reject) => { | |
| 50 | + request({ | |
| 51 | + url: '/complaintType.saveComplaintType', | |
| 52 | + method: 'post', | |
| 53 | + data | |
| 54 | + }).then(response => { | |
| 55 | + const res = response.data | |
| 56 | + resolve(res) | |
| 57 | + }).catch(error => { | |
| 58 | + reject(error) | |
| 59 | + }) | |
| 60 | + }) | |
| 61 | +} | |
| 0 | 62 | \ No newline at end of file | ... | ... |
src/api/oa/complaintApi.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 3 | + | |
| 4 | +// 获取投诉列表 | |
| 5 | +export function listComplaints(params) { | |
| 6 | + return new Promise((resolve, reject) => { | |
| 7 | + request({ | |
| 8 | + url: '/complaint.listComplaints', | |
| 9 | + method: 'get', | |
| 10 | + params: { | |
| 11 | + ...params, | |
| 12 | + communityId: getCommunityId() | |
| 13 | + } | |
| 14 | + }).then(response => { | |
| 15 | + const res = response.data | |
| 16 | + resolve(res) | |
| 17 | + }).catch(error => { | |
| 18 | + reject(error) | |
| 19 | + }) | |
| 20 | + }) | |
| 21 | +} | |
| 22 | + | |
| 23 | +// 获取投诉类型列表 | |
| 24 | +export function listComplaintTypes(params) { | |
| 25 | + return new Promise((resolve, reject) => { | |
| 26 | + request({ | |
| 27 | + url: '/complaintType.listComplaintType', | |
| 28 | + method: 'get', | |
| 29 | + params: { | |
| 30 | + ...params, | |
| 31 | + communityId: getCommunityId() | |
| 32 | + } | |
| 33 | + }).then(response => { | |
| 34 | + const res = response.data | |
| 35 | + resolve(res) | |
| 36 | + }).catch(error => { | |
| 37 | + reject(error) | |
| 38 | + }) | |
| 39 | + }) | |
| 40 | +} | |
| 41 | + | |
| 42 | +// 更新投诉信息 | |
| 43 | +export function updateComplaint(data) { | |
| 44 | + return new Promise((resolve, reject) => { | |
| 45 | + request({ | |
| 46 | + url: '/complaint.updateComplaint', | |
| 47 | + method: 'post', | |
| 48 | + data: { | |
| 49 | + ...data, | |
| 50 | + communityId: getCommunityId() | |
| 51 | + } | |
| 52 | + }).then(response => { | |
| 53 | + const res = response.data | |
| 54 | + resolve(res) | |
| 55 | + }).catch(error => { | |
| 56 | + reject(error) | |
| 57 | + }) | |
| 58 | + }) | |
| 59 | +} | |
| 60 | + | |
| 61 | +// 删除投诉 | |
| 62 | +export function deleteComplaint(data) { | |
| 63 | + return new Promise((resolve, reject) => { | |
| 64 | + request({ | |
| 65 | + url: '/complaint.deleteComplaint', | |
| 66 | + method: 'post', | |
| 67 | + data: { | |
| 68 | + ...data, | |
| 69 | + communityId: getCommunityId() | |
| 70 | + } | |
| 71 | + }).then(response => { | |
| 72 | + const res = response.data | |
| 73 | + resolve(res) | |
| 74 | + }).catch(error => { | |
| 75 | + reject(error) | |
| 76 | + }) | |
| 77 | + }) | |
| 78 | +} | |
| 79 | + | |
| 80 | +// 获取工作流审核信息 | |
| 81 | +export function listWorkflowAuditInfo(params) { | |
| 82 | + return new Promise((resolve, reject) => { | |
| 83 | + request({ | |
| 84 | + url: '/workflow.listWorkflowAuditInfo', | |
| 85 | + method: 'get', | |
| 86 | + params: { | |
| 87 | + ...params, | |
| 88 | + communityId: getCommunityId() | |
| 89 | + } | |
| 90 | + }).then(response => { | |
| 91 | + const res = response.data | |
| 92 | + resolve(res) | |
| 93 | + }).catch(error => { | |
| 94 | + reject(error) | |
| 95 | + }) | |
| 96 | + }) | |
| 97 | +} | |
| 0 | 98 | \ No newline at end of file | ... | ... |
src/api/oa/complaintTypeApi.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 3 | + | |
| 4 | +// 获取投诉类型列表 | |
| 5 | +export function listComplaintType(params) { | |
| 6 | + return new Promise((resolve, reject) => { | |
| 7 | + request({ | |
| 8 | + url: '/complaintType.listComplaintType', | |
| 9 | + method: 'get', | |
| 10 | + params: { | |
| 11 | + ...params, | |
| 12 | + communityId: getCommunityId() | |
| 13 | + } | |
| 14 | + }).then(response => { | |
| 15 | + const res = response.data | |
| 16 | + resolve(res) | |
| 17 | + }).catch(error => { | |
| 18 | + reject(error) | |
| 19 | + }) | |
| 20 | + }) | |
| 21 | +} | |
| 22 | + | |
| 23 | +// 删除投诉类型 | |
| 24 | +export function deleteComplaintType(data) { | |
| 25 | + return new Promise((resolve, reject) => { | |
| 26 | + request({ | |
| 27 | + url: '/complaintType.deleteComplaintType', | |
| 28 | + method: 'post', | |
| 29 | + data: { | |
| 30 | + ...data, | |
| 31 | + communityId: getCommunityId() | |
| 32 | + } | |
| 33 | + }).then(response => { | |
| 34 | + const res = response.data | |
| 35 | + resolve(res) | |
| 36 | + }).catch(error => { | |
| 37 | + reject(error) | |
| 38 | + }) | |
| 39 | + }) | |
| 40 | +} | |
| 41 | + | |
| 42 | +// 添加投诉类型 | |
| 43 | +export function addComplaintType(data) { | |
| 44 | + return new Promise((resolve, reject) => { | |
| 45 | + request({ | |
| 46 | + url: '/complaintType.saveComplaintType', | |
| 47 | + method: 'post', | |
| 48 | + data: { | |
| 49 | + ...data, | |
| 50 | + communityId: getCommunityId() | |
| 51 | + } | |
| 52 | + }).then(response => { | |
| 53 | + const res = response.data | |
| 54 | + resolve(res) | |
| 55 | + }).catch(error => { | |
| 56 | + reject(error) | |
| 57 | + }) | |
| 58 | + }) | |
| 59 | +} | |
| 60 | + | |
| 61 | +// 更新投诉类型 | |
| 62 | +export function updateComplaintType(data) { | |
| 63 | + return new Promise((resolve, reject) => { | |
| 64 | + request({ | |
| 65 | + url: '/complaintType.updateComplaintType', | |
| 66 | + method: 'post', | |
| 67 | + data: { | |
| 68 | + ...data, | |
| 69 | + communityId: getCommunityId() | |
| 70 | + } | |
| 71 | + }).then(response => { | |
| 72 | + const res = response.data | |
| 73 | + resolve(res) | |
| 74 | + }).catch(error => { | |
| 75 | + reject(error) | |
| 76 | + }) | |
| 77 | + }) | |
| 78 | +} | |
| 79 | + | |
| 80 | +// 获取单个投诉类型详情 | |
| 81 | +export function getComplaintTypeDetail(typeCd) { | |
| 82 | + return new Promise((resolve, reject) => { | |
| 83 | + request({ | |
| 84 | + url: '/complaintType.getComplaintType', | |
| 85 | + method: 'get', | |
| 86 | + params: { | |
| 87 | + typeCd, | |
| 88 | + communityId: getCommunityId() | |
| 89 | + } | |
| 90 | + }).then(response => { | |
| 91 | + const res = response.data | |
| 92 | + resolve(res) | |
| 93 | + }).catch(error => { | |
| 94 | + reject(error) | |
| 95 | + }) | |
| 96 | + }) | |
| 97 | +} | |
| 0 | 98 | \ No newline at end of file | ... | ... |
src/api/oa/editComplaintTypeApi.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 3 | + | |
| 4 | +// 获取投诉类型详情 | |
| 5 | +export function getComplaintTypeDetail(params) { | |
| 6 | + return new Promise((resolve, reject) => { | |
| 7 | + request({ | |
| 8 | + url: '/complaintType.listComplaintType', | |
| 9 | + method: 'get', | |
| 10 | + params: { | |
| 11 | + ...params, | |
| 12 | + communityId: params.communityId || getCommunityId() | |
| 13 | + } | |
| 14 | + }).then(response => { | |
| 15 | + const res = response.data | |
| 16 | + resolve(res) | |
| 17 | + }).catch(error => { | |
| 18 | + reject(error) | |
| 19 | + }) | |
| 20 | + }) | |
| 21 | +} | |
| 22 | + | |
| 23 | +// 更新投诉类型 | |
| 24 | +export function updateComplaintType(data) { | |
| 25 | + return new Promise((resolve, reject) => { | |
| 26 | + request({ | |
| 27 | + url: '/complaintType.updateComplaintType', | |
| 28 | + method: 'post', | |
| 29 | + data: { | |
| 30 | + ...data, | |
| 31 | + communityId: data.communityId || getCommunityId() | |
| 32 | + } | |
| 33 | + }).then(response => { | |
| 34 | + const res = response.data | |
| 35 | + resolve(res) | |
| 36 | + }).catch(error => { | |
| 37 | + reject(error) | |
| 38 | + }) | |
| 39 | + }) | |
| 40 | +} | |
| 41 | + | |
| 42 | +// 获取组织树 | |
| 43 | +export function listOrgTree(params) { | |
| 44 | + return new Promise((resolve, reject) => { | |
| 45 | + request({ | |
| 46 | + url: '/org.listOrgTree', | |
| 47 | + method: 'get', | |
| 48 | + params: { | |
| 49 | + ...params, | |
| 50 | + communityId: params.communityId || getCommunityId() | |
| 51 | + } | |
| 52 | + }).then(response => { | |
| 53 | + const res = response.data | |
| 54 | + resolve(res) | |
| 55 | + }).catch(error => { | |
| 56 | + reject(error) | |
| 57 | + }) | |
| 58 | + }) | |
| 59 | +} | |
| 60 | + | |
| 61 | +// 获取员工信息 | |
| 62 | +export function getStaffInfos(params) { | |
| 63 | + return new Promise((resolve, reject) => { | |
| 64 | + request({ | |
| 65 | + url: '/query.staff.infos', | |
| 66 | + method: 'get', | |
| 67 | + params: { | |
| 68 | + ...params, | |
| 69 | + communityId: params.communityId || getCommunityId() | |
| 70 | + } | |
| 71 | + }).then(response => { | |
| 72 | + const res = response.data | |
| 73 | + resolve(res) | |
| 74 | + }).catch(error => { | |
| 75 | + reject(error) | |
| 76 | + }) | |
| 77 | + }) | |
| 78 | +} | |
| 0 | 79 | \ No newline at end of file | ... | ... |
src/components/oa/complaintDetail.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog :title="`${form.typeCdName} ${$t('complaint.detail.title')}`" :visible.sync="visible" width="70%" top="5vh"> | |
| 3 | + <el-form label-width="120px"> | |
| 4 | + <el-row :gutter="20"> | |
| 5 | + <el-col :span="12"> | |
| 6 | + <el-form-item :label="$t('complaint.detail.room')"> | |
| 7 | + <el-input v-model="form.roomName" readonly /> | |
| 8 | + </el-form-item> | |
| 9 | + </el-col> | |
| 10 | + <el-col :span="12"> | |
| 11 | + <el-form-item :label="$t('complaint.detail.id')"> | |
| 12 | + <el-input v-model="form.complaintId" readonly /> | |
| 13 | + </el-form-item> | |
| 14 | + </el-col> | |
| 15 | + </el-row> | |
| 16 | + | |
| 17 | + <el-row :gutter="20"> | |
| 18 | + <el-col :span="12"> | |
| 19 | + <el-form-item :label="$t('complaint.detail.type')"> | |
| 20 | + <el-input v-model="form.typeCdName" readonly /> | |
| 21 | + </el-form-item> | |
| 22 | + </el-col> | |
| 23 | + <el-col :span="12"> | |
| 24 | + <el-form-item :label="$t('complaint.detail.status')"> | |
| 25 | + <el-input v-model="form.stateName" readonly /> | |
| 26 | + </el-form-item> | |
| 27 | + </el-col> | |
| 28 | + </el-row> | |
| 29 | + | |
| 30 | + <el-row :gutter="20"> | |
| 31 | + <el-col :span="12"> | |
| 32 | + <el-form-item :label="$t('complaint.detail.contact')"> | |
| 33 | + <el-input v-model="form.complaintName" readonly /> | |
| 34 | + </el-form-item> | |
| 35 | + </el-col> | |
| 36 | + <el-col :span="12"> | |
| 37 | + <el-form-item :label="$t('complaint.detail.phone')"> | |
| 38 | + <el-input v-model="form.tel" readonly /> | |
| 39 | + </el-form-item> | |
| 40 | + </el-col> | |
| 41 | + </el-row> | |
| 42 | + | |
| 43 | + <template v-if="form.showCurrentUser"> | |
| 44 | + <el-row :gutter="20"> | |
| 45 | + <el-col :span="12"> | |
| 46 | + <el-form-item :label="$t('complaint.detail.handler')"> | |
| 47 | + <el-input v-model="form.currentUserName" readonly /> | |
| 48 | + </el-form-item> | |
| 49 | + </el-col> | |
| 50 | + <el-col :span="12"> | |
| 51 | + <el-form-item :label="$t('complaint.detail.handlerPhone')"> | |
| 52 | + <el-input v-model="form.currentUserTel" readonly /> | |
| 53 | + </el-form-item> | |
| 54 | + </el-col> | |
| 55 | + </el-row> | |
| 56 | + <el-form-item :label="$t('complaint.detail.handlerId')"> | |
| 57 | + <el-input v-model="form.currentUserId" readonly /> | |
| 58 | + </el-form-item> | |
| 59 | + </template> | |
| 60 | + | |
| 61 | + <el-form-item :label="`${form.typeCdName}${$t('complaint.detail.content')}`"> | |
| 62 | + <el-input type="textarea" :rows="4" v-model="form.context" readonly /> | |
| 63 | + </el-form-item> | |
| 64 | + | |
| 65 | + <el-form-item :label="$t('complaint.detail.photos')" v-if="form.photosShow.length > 0"> | |
| 66 | + <div style="display:flex;flex-wrap:wrap;gap:10px"> | |
| 67 | + <el-image v-for="(photo, index) in form.photosShow" :key="index" style="width:100px;height:100px" | |
| 68 | + :src="photo.url" :preview-src-list="form.photosShow.map(p => p.url)" fit="cover" /> | |
| 69 | + </div> | |
| 70 | + </el-form-item> | |
| 71 | + </el-form> | |
| 72 | + | |
| 73 | + <el-table :data="form.comments" border style="width:100%;margin-top:20px"> | |
| 74 | + <el-table-column prop="index" :label="$t('complaint.detail.serial')" width="80" align="center"> | |
| 75 | + <template slot-scope="scope"> | |
| 76 | + {{ scope.$index + 1 }} | |
| 77 | + </template> | |
| 78 | + </el-table-column> | |
| 79 | + <el-table-column prop="auditName" :label="$t('complaint.detail.handler')" align="center" /> | |
| 80 | + <el-table-column prop="stateName" :label="$t('complaint.detail.status')" align="center" /> | |
| 81 | + <el-table-column prop="auditTime" :label="$t('complaint.detail.handleTime')" align="center" /> | |
| 82 | + <el-table-column prop="duration" :label="$t('complaint.detail.duration')" align="center" /> | |
| 83 | + <el-table-column prop="message" :label="$t('complaint.detail.comment')" align="center" /> | |
| 84 | + </el-table> | |
| 85 | + </el-dialog> | |
| 86 | +</template> | |
| 87 | + | |
| 88 | +<script> | |
| 89 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 90 | +import { listWorkflowAuditInfo } from '@/api/oa/complaintApi' | |
| 91 | + | |
| 92 | +export default { | |
| 93 | + name: 'ComplaintDetail', | |
| 94 | + data() { | |
| 95 | + return { | |
| 96 | + visible: false, | |
| 97 | + form: { | |
| 98 | + complaintId: '', | |
| 99 | + typeCd: '', | |
| 100 | + complaintName: '', | |
| 101 | + tel: '', | |
| 102 | + context: '', | |
| 103 | + typeCdName: '', | |
| 104 | + stateName: '', | |
| 105 | + roomName: '', | |
| 106 | + currentUserName: '', | |
| 107 | + currentUserTel: '', | |
| 108 | + currentUserId: '', | |
| 109 | + showCurrentUser: true, | |
| 110 | + photos: [], | |
| 111 | + photosShow: [], | |
| 112 | + comments: [] | |
| 113 | + } | |
| 114 | + } | |
| 115 | + }, | |
| 116 | + methods: { | |
| 117 | + open(row) { | |
| 118 | + this.visible = true | |
| 119 | + this.form = { | |
| 120 | + ...row, | |
| 121 | + photosShow: row.photos.map(photo => ({ | |
| 122 | + url: `/callComponent/download/getFile/file?fileId=${photo.url}&communityId=-1&time=${new Date()}` | |
| 123 | + })), | |
| 124 | + showCurrentUser: row['currentUserName'], | |
| 125 | + currentUserName: row.currentUserName || this.$t('common.none'), | |
| 126 | + currentUserTel: row.currentUserTel || this.$t('common.none'), | |
| 127 | + currentUserId: row.currentUserId || this.$t('common.none') | |
| 128 | + } | |
| 129 | + this.loadComments() | |
| 130 | + }, | |
| 131 | + async loadComments() { | |
| 132 | + try { | |
| 133 | + const params = { | |
| 134 | + communityId: getCommunityId(), | |
| 135 | + businessKey: this.form.complaintId | |
| 136 | + } | |
| 137 | + const { data } = await listWorkflowAuditInfo(params) | |
| 138 | + this.form.comments = data | |
| 139 | + } catch (error) { | |
| 140 | + console.error('获取处理记录失败:', error) | |
| 141 | + } | |
| 142 | + } | |
| 143 | + } | |
| 144 | +} | |
| 145 | +</script> | |
| 0 | 146 | \ No newline at end of file | ... | ... |
src/components/oa/deleteComplaint.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog | |
| 3 | + :title="$t('complaint.delete.title')" | |
| 4 | + :visible.sync="visible" | |
| 5 | + width="30%" | |
| 6 | + center | |
| 7 | + > | |
| 8 | + <div style="text-align:center"> | |
| 9 | + <p>{{ $t('complaint.delete.confirm') }}</p> | |
| 10 | + </div> | |
| 11 | + <span slot="footer" class="dialog-footer"> | |
| 12 | + <el-button @click="visible = false">{{ $t('common.cancel') }}</el-button> | |
| 13 | + <el-button type="primary" @click="handleDelete" :loading="loading">{{ $t('common.confirm') }}</el-button> | |
| 14 | + </span> | |
| 15 | + </el-dialog> | |
| 16 | +</template> | |
| 17 | + | |
| 18 | +<script> | |
| 19 | +import { deleteComplaint } from '@/api/oa/complaintApi' | |
| 20 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 21 | + | |
| 22 | +export default { | |
| 23 | + name: 'DeleteComplaint', | |
| 24 | + data() { | |
| 25 | + return { | |
| 26 | + visible: false, | |
| 27 | + loading: false, | |
| 28 | + complaintId: '', | |
| 29 | + communityId: '' | |
| 30 | + } | |
| 31 | + }, | |
| 32 | + methods: { | |
| 33 | + open(row) { | |
| 34 | + this.communityId = getCommunityId() | |
| 35 | + this.complaintId = row.complaintId | |
| 36 | + this.visible = true | |
| 37 | + }, | |
| 38 | + async handleDelete() { | |
| 39 | + try { | |
| 40 | + this.loading = true | |
| 41 | + await deleteComplaint({ | |
| 42 | + complaintId: this.complaintId, | |
| 43 | + communityId: this.communityId | |
| 44 | + }) | |
| 45 | + this.$message.success(this.$t('complaint.delete.success')) | |
| 46 | + this.$emit('success') | |
| 47 | + this.visible = false | |
| 48 | + } catch (error) { | |
| 49 | + console.error('删除投诉失败:', error) | |
| 50 | + } finally { | |
| 51 | + this.loading = false | |
| 52 | + } | |
| 53 | + } | |
| 54 | + } | |
| 55 | +} | |
| 56 | +</script> | |
| 0 | 57 | \ No newline at end of file | ... | ... |
src/components/oa/deleteComplaintType.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog | |
| 3 | + :title="$t('deleteComplaintType.confirmTitle')" | |
| 4 | + :visible.sync="visible" | |
| 5 | + width="30%" | |
| 6 | + @close="handleClose" | |
| 7 | + > | |
| 8 | + <div style="text-align: center;"> | |
| 9 | + <p>{{ $t('deleteComplaintType.confirmText') }}</p> | |
| 10 | + </div> | |
| 11 | + <span slot="footer" class="dialog-footer"> | |
| 12 | + <el-button @click="handleClose">{{ $t('deleteComplaintType.cancel') }}</el-button> | |
| 13 | + <el-button type="primary" @click="deleteComplaintType">{{ $t('deleteComplaintType.confirm') }}</el-button> | |
| 14 | + </span> | |
| 15 | + </el-dialog> | |
| 16 | +</template> | |
| 17 | + | |
| 18 | +<script> | |
| 19 | +import { deleteComplaintType } from '@/api/oa/complaintTypeApi' | |
| 20 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 21 | + | |
| 22 | +export default { | |
| 23 | + name: 'DeleteComplaintType', | |
| 24 | + data() { | |
| 25 | + return { | |
| 26 | + visible: false, | |
| 27 | + currentComplaintType: null | |
| 28 | + } | |
| 29 | + }, | |
| 30 | + methods: { | |
| 31 | + open(complaintType) { | |
| 32 | + this.currentComplaintType = complaintType | |
| 33 | + this.visible = true | |
| 34 | + }, | |
| 35 | + async deleteComplaintType() { | |
| 36 | + try { | |
| 37 | + const params = { | |
| 38 | + ...this.currentComplaintType, | |
| 39 | + communityId: getCommunityId() | |
| 40 | + } | |
| 41 | + await deleteComplaintType(params) | |
| 42 | + this.$emit('success') | |
| 43 | + this.visible = false | |
| 44 | + this.$message.success(this.$t('deleteComplaintType.success')) | |
| 45 | + } catch (error) { | |
| 46 | + this.$message.error(this.$t('deleteComplaintType.error')) | |
| 47 | + } | |
| 48 | + }, | |
| 49 | + handleClose() { | |
| 50 | + this.visible = false | |
| 51 | + } | |
| 52 | + } | |
| 53 | +} | |
| 54 | +</script> | |
| 0 | 55 | \ No newline at end of file | ... | ... |
src/components/oa/editComplaint.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog | |
| 3 | + :title="$t('complaint.edit.title')" | |
| 4 | + :visible.sync="visible" | |
| 5 | + width="50%" | |
| 6 | + @close="handleClose" | |
| 7 | + > | |
| 8 | + <el-form | |
| 9 | + ref="form" | |
| 10 | + :model="form" | |
| 11 | + label-width="120px" | |
| 12 | + :rules="rules" | |
| 13 | + > | |
| 14 | + <el-form-item | |
| 15 | + :label="$t('complaint.edit.type')" | |
| 16 | + prop="typeCd" | |
| 17 | + > | |
| 18 | + <el-select | |
| 19 | + v-model="form.typeCd" | |
| 20 | + style="width:100%" | |
| 21 | + :placeholder="$t('complaint.edit.typePlaceholder')" | |
| 22 | + > | |
| 23 | + <el-option | |
| 24 | + v-for="item in complaintTypes" | |
| 25 | + :key="item.typeCd" | |
| 26 | + :label="item.typeName" | |
| 27 | + :value="item.typeCd" | |
| 28 | + /> | |
| 29 | + </el-select> | |
| 30 | + </el-form-item> | |
| 31 | + <el-form-item | |
| 32 | + :label="$t('complaint.edit.contact')" | |
| 33 | + prop="complaintName" | |
| 34 | + > | |
| 35 | + <el-input | |
| 36 | + v-model="form.complaintName" | |
| 37 | + :placeholder="$t('complaint.edit.contactPlaceholder')" | |
| 38 | + /> | |
| 39 | + </el-form-item> | |
| 40 | + <el-form-item | |
| 41 | + :label="$t('complaint.edit.phone')" | |
| 42 | + prop="tel" | |
| 43 | + > | |
| 44 | + <el-input | |
| 45 | + v-model="form.tel" | |
| 46 | + :placeholder="$t('complaint.edit.phonePlaceholder')" | |
| 47 | + /> | |
| 48 | + </el-form-item> | |
| 49 | + <el-form-item | |
| 50 | + :label="$t('complaint.edit.content')" | |
| 51 | + prop="context" | |
| 52 | + > | |
| 53 | + <el-input | |
| 54 | + type="textarea" | |
| 55 | + :rows="4" | |
| 56 | + v-model="form.context" | |
| 57 | + :placeholder="$t('complaint.edit.contentPlaceholder')" | |
| 58 | + /> | |
| 59 | + </el-form-item> | |
| 60 | + </el-form> | |
| 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 { updateComplaint } from '@/api/oa/complaintApi' | |
| 70 | +import { listComplaintTypes } from '@/api/oa/complaintApi' | |
| 71 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 72 | + | |
| 73 | +export default { | |
| 74 | + name: 'EditComplaint', | |
| 75 | + data() { | |
| 76 | + return { | |
| 77 | + visible: false, | |
| 78 | + communityId: '', | |
| 79 | + complaintTypes: [], | |
| 80 | + form: { | |
| 81 | + complaintId: '', | |
| 82 | + typeCd: '', | |
| 83 | + complaintName: '', | |
| 84 | + tel: '', | |
| 85 | + context: '', | |
| 86 | + communityId: '' | |
| 87 | + }, | |
| 88 | + rules: { | |
| 89 | + typeCd: [ | |
| 90 | + { required: true, message: this.$t('complaint.validate.type'), trigger: 'blur' } | |
| 91 | + ], | |
| 92 | + complaintName: [ | |
| 93 | + { required: true, message: this.$t('complaint.validate.contact'), trigger: 'blur' }, | |
| 94 | + { max: 200, message: this.$t('complaint.validate.contactMax'), trigger: 'blur' } | |
| 95 | + ], | |
| 96 | + tel: [ | |
| 97 | + { required: true, message: this.$t('complaint.validate.phone'), trigger: 'blur' }, | |
| 98 | + { pattern: /^1[3-9]\d{9}$/, message: this.$t('complaint.validate.phoneFormat'), trigger: 'blur' } | |
| 99 | + ], | |
| 100 | + context: [ | |
| 101 | + { required: true, message: this.$t('complaint.validate.content'), trigger: 'blur' }, | |
| 102 | + { max: 4000, message: this.$t('complaint.validate.contentMax'), trigger: 'blur' } | |
| 103 | + ] | |
| 104 | + } | |
| 105 | + } | |
| 106 | + }, | |
| 107 | + methods: { | |
| 108 | + open(row) { | |
| 109 | + this.communityId = getCommunityId() | |
| 110 | + this.form = { | |
| 111 | + ...row, | |
| 112 | + communityId: this.communityId | |
| 113 | + } | |
| 114 | + this.getComplaintTypes() | |
| 115 | + this.visible = true | |
| 116 | + }, | |
| 117 | + async getComplaintTypes() { | |
| 118 | + try { | |
| 119 | + const params = { | |
| 120 | + page: 1, | |
| 121 | + row: 100, | |
| 122 | + communityId: this.communityId | |
| 123 | + } | |
| 124 | + const { data } = await listComplaintTypes(params) | |
| 125 | + this.complaintTypes = data | |
| 126 | + } catch (error) { | |
| 127 | + console.error('获取投诉类型失败:', error) | |
| 128 | + } | |
| 129 | + }, | |
| 130 | + handleSubmit() { | |
| 131 | + this.$refs.form.validate(async valid => { | |
| 132 | + if (valid) { | |
| 133 | + try { | |
| 134 | + await updateComplaint(this.form) | |
| 135 | + this.$message.success(this.$t('complaint.edit.success')) | |
| 136 | + this.$emit('success') | |
| 137 | + this.visible = false | |
| 138 | + } catch (error) { | |
| 139 | + console.error('更新投诉失败:', error) | |
| 140 | + } | |
| 141 | + } | |
| 142 | + }) | |
| 143 | + }, | |
| 144 | + handleClose() { | |
| 145 | + this.$refs.form.resetFields() | |
| 146 | + } | |
| 147 | + } | |
| 148 | +} | |
| 149 | +</script> | |
| 0 | 150 | \ No newline at end of file | ... | ... |
src/components/staff/selectStaffsDiv.vue
| 1 | 1 | <template> |
| 2 | - <el-row :gutter="20"> | |
| 3 | - <el-col :span="6" class="border-right"> | |
| 4 | - <div class="text-center"> | |
| 5 | - <h4>{{ $t('selectStaff.orgInfo') }}</h4> | |
| 2 | + <el-row :gutter="20"> | |
| 3 | + <el-col :span="6" class="border-right"> | |
| 4 | + <div class="text-center"> | |
| 5 | + <h4>{{ $t('selectStaff.orgInfo') }}</h4> | |
| 6 | + </div> | |
| 7 | + <div class="tree-container"> | |
| 8 | + <org-tree-show @org-selected="handleNodeClick" /> | |
| 9 | + </div> | |
| 10 | + </el-col> | |
| 11 | + | |
| 12 | + <el-col :span="6" class="border-right"> | |
| 13 | + <div class="text-center"> | |
| 14 | + <h4>{{ $t('selectStaff.staffInfo') }}</h4> | |
| 15 | + </div> | |
| 16 | + <div class="staff-list"> | |
| 17 | + <div v-for="staff in staffList" :key="staff.staffId" class="staff-item" | |
| 18 | + :class="{ selected: currentStaffId === staff.userId }" @click="selectStaff(staff)"> | |
| 19 | + <i class="el-icon-user"></i> | |
| 20 | + <span>{{ staff.name }}</span> | |
| 21 | + <div>{{ staff.tel }}</div> | |
| 6 | 22 | </div> |
| 7 | - <div class="tree-container"> | |
| 8 | - <org-tree-show @org-selected="handleNodeClick" /> | |
| 23 | + </div> | |
| 24 | + </el-col> | |
| 25 | + | |
| 26 | + <el-col :span="6"> | |
| 27 | + <div class="text-center"> | |
| 28 | + <h4>{{ $t('selectStaff.selectedStaff') }}</h4> | |
| 29 | + </div> | |
| 30 | + <div class="selected-staff"> | |
| 31 | + <div v-for="staff in selectedStaffs" :key="staff.userId" class="selected-item"> | |
| 32 | + <span>{{ staff.name }}</span> | |
| 33 | + <i class="el-icon-close remove-icon" @click="removeStaff(staff)"></i> | |
| 9 | 34 | </div> |
| 10 | - </el-col> | |
| 11 | - | |
| 12 | - <el-col :span="6" class="border-right"> | |
| 13 | - <div class="text-center"> | |
| 14 | - <h4>{{ $t('selectStaff.staffInfo') }}</h4> | |
| 15 | - </div> | |
| 16 | - <div class="staff-list"> | |
| 17 | - <div | |
| 18 | - v-for="staff in staffList" | |
| 19 | - :key="staff.staffId" | |
| 20 | - class="staff-item" | |
| 21 | - :class="{ selected: currentStaffId === staff.userId }" | |
| 22 | - @click="selectStaff(staff)" | |
| 23 | - > | |
| 24 | - <i class="el-icon-user"></i> | |
| 25 | - <span>{{ staff.name }}</span> | |
| 26 | - <div>{{ staff.tel }}</div> | |
| 27 | - </div> | |
| 28 | - </div> | |
| 29 | - </el-col> | |
| 30 | - | |
| 31 | - <el-col :span="6"> | |
| 32 | - <div class="text-center"> | |
| 33 | - <h4>{{ $t('selectStaff.selectedStaff') }}</h4> | |
| 34 | - </div> | |
| 35 | - <div class="selected-staff"> | |
| 36 | - <div v-for="staff in selectedStaffs" :key="staff.userId" class="selected-item"> | |
| 37 | - <span>{{ staff.name }}</span> | |
| 38 | - <i class="el-icon-close remove-icon" @click="removeStaff(staff)"></i> | |
| 39 | - </div> | |
| 40 | - </div> | |
| 41 | - </el-col> | |
| 42 | - </el-row> | |
| 43 | - </template> | |
| 44 | - | |
| 45 | - <script> | |
| 46 | - import { listStaffsByOrgId } from '@/api/inspection/inspectionPlanApi' | |
| 47 | - import OrgTreeShow from '@/components/org/OrgTreeShow' | |
| 35 | + </div> | |
| 36 | + </el-col> | |
| 37 | + </el-row> | |
| 38 | +</template> | |
| 48 | 39 | |
| 49 | - export default { | |
| 50 | - name: 'SelectStaffs', | |
| 51 | - components: { | |
| 52 | - OrgTreeShow | |
| 40 | +<script> | |
| 41 | +import { listStaffsByOrgId } from '@/api/inspection/inspectionPlanApi' | |
| 42 | +import OrgTreeShow from '@/components/org/OrgTreeShow' | |
| 43 | + | |
| 44 | +export default { | |
| 45 | + name: 'SelectStaffs', | |
| 46 | + components: { | |
| 47 | + OrgTreeShow | |
| 48 | + }, | |
| 49 | + data() { | |
| 50 | + return { | |
| 51 | + staffList: [], | |
| 52 | + selectedStaffs: [], | |
| 53 | + currentStaffId: null, | |
| 54 | + currentOrgId: null | |
| 55 | + } | |
| 56 | + }, | |
| 57 | + methods: { | |
| 58 | + setStaffs(staffs) { | |
| 59 | + this.selectedStaffs = [...staffs] | |
| 60 | + }, | |
| 61 | + | |
| 62 | + getSelectedStaffs() { | |
| 63 | + return this.selectedStaffs | |
| 53 | 64 | }, |
| 54 | - data() { | |
| 55 | - return { | |
| 56 | - staffList: [], | |
| 57 | - selectedStaffs: [], | |
| 58 | - currentStaffId: null, | |
| 59 | - currentOrgId: null | |
| 65 | + | |
| 66 | + async handleNodeClick(node) { | |
| 67 | + this.currentOrgId = node.id | |
| 68 | + try { | |
| 69 | + const { staffs } = await listStaffsByOrgId({ | |
| 70 | + orgId: node.id, | |
| 71 | + page: 1, | |
| 72 | + row: 100 | |
| 73 | + }) | |
| 74 | + this.staffList = staffs | |
| 75 | + } catch (error) { | |
| 76 | + console.error('Failed to load staffs:', error) | |
| 60 | 77 | } |
| 61 | 78 | }, |
| 62 | - methods: { | |
| 63 | - setStaffs(staffs) { | |
| 64 | - this.selectedStaffs = [...staffs] | |
| 65 | - }, | |
| 66 | - | |
| 67 | - getSelectedStaffs() { | |
| 68 | - return this.selectedStaffs | |
| 69 | - }, | |
| 70 | - | |
| 71 | - async handleNodeClick(node) { | |
| 72 | - this.currentOrgId = node.id | |
| 73 | - try { | |
| 74 | - const { staffs } = await listStaffsByOrgId({ | |
| 75 | - orgId: node.id, | |
| 76 | - page: 1, | |
| 77 | - row: 100 | |
| 78 | - }) | |
| 79 | - this.staffList = staffs | |
| 80 | - } catch (error) { | |
| 81 | - console.error('Failed to load staffs:', error) | |
| 82 | - } | |
| 83 | - }, | |
| 84 | - | |
| 85 | - selectStaff(staff) { | |
| 86 | - this.currentStaffId = staff.userId | |
| 87 | - // 检查是否已选择 | |
| 88 | - const isSelected = this.selectedStaffs.some(s => s.userId === staff.userId) | |
| 89 | - if (!isSelected) { | |
| 90 | - this.selectedStaffs.push({ | |
| 91 | - userId: staff.userId, | |
| 92 | - name: staff.userName | |
| 93 | - }) | |
| 94 | - } | |
| 95 | - this.$emit('selectStaffs', this.selectedStaffs) | |
| 96 | - }, | |
| 97 | - | |
| 98 | - removeStaff(staff) { | |
| 99 | - this.selectedStaffs = this.selectedStaffs.filter(s => s.staffId !== staff.staffId) | |
| 79 | + | |
| 80 | + selectStaff(staff) { | |
| 81 | + this.currentStaffId = staff.userId | |
| 82 | + // 检查是否已选择 | |
| 83 | + const isSelected = this.selectedStaffs.some(s => s.userId === staff.userId) | |
| 84 | + if (!isSelected) { | |
| 85 | + this.selectedStaffs.push({ | |
| 86 | + userId: staff.userId, | |
| 87 | + name: staff.userName | |
| 88 | + }) | |
| 100 | 89 | } |
| 90 | + this.$emit('selectStaffs', this.selectedStaffs) | |
| 91 | + }, | |
| 92 | + | |
| 93 | + removeStaff(staff) { | |
| 94 | + this.selectedStaffs = this.selectedStaffs.filter(s => s.staffId !== staff.staffId) | |
| 101 | 95 | } |
| 102 | 96 | } |
| 103 | - </script> | |
| 97 | +} | |
| 98 | +</script> | |
| 104 | 99 | |
| 105 | - <style scoped> | |
| 106 | - .border-right { | |
| 107 | - border-right: 1px solid #eee; | |
| 108 | - } | |
| 109 | - .tree-container { | |
| 110 | - height: 300px; | |
| 111 | - overflow: auto; | |
| 112 | - padding: 10px; | |
| 113 | - } | |
| 114 | - .staff-list { | |
| 115 | - height: 300px; | |
| 116 | - overflow: auto; | |
| 117 | - padding: 10px; | |
| 118 | - } | |
| 119 | - .staff-item { | |
| 120 | - padding: 10px; | |
| 121 | - cursor: pointer; | |
| 122 | - border-bottom: 1px solid #eee; | |
| 123 | - } | |
| 124 | - .staff-item:hover { | |
| 125 | - background-color: #f5f7fa; | |
| 126 | - } | |
| 127 | - .staff-item.selected { | |
| 128 | - background-color: #ecf5ff; | |
| 129 | - } | |
| 130 | - .selected-staff { | |
| 131 | - height: 300px; | |
| 132 | - overflow: auto; | |
| 133 | - padding: 10px; | |
| 134 | - } | |
| 135 | - .selected-item { | |
| 136 | - display: flex; | |
| 137 | - justify-content: space-between; | |
| 138 | - align-items: center; | |
| 139 | - padding: 8px; | |
| 140 | - margin-bottom: 5px; | |
| 141 | - border: 1px solid #ebeef5; | |
| 142 | - border-radius: 4px; | |
| 143 | - } | |
| 144 | - .remove-icon { | |
| 145 | - cursor: pointer; | |
| 146 | - color: #f56c6c; | |
| 147 | - } | |
| 148 | - .text-center { | |
| 149 | - text-align: center; | |
| 150 | - padding: 10px 0; | |
| 151 | - } | |
| 152 | - </style> | |
| 153 | 100 | \ No newline at end of file |
| 101 | +<style scoped> | |
| 102 | +.border-right { | |
| 103 | + border-right: 1px solid #eee; | |
| 104 | +} | |
| 105 | + | |
| 106 | +.tree-container { | |
| 107 | + height: 300px; | |
| 108 | + overflow: auto; | |
| 109 | + padding: 10px; | |
| 110 | +} | |
| 111 | + | |
| 112 | +.staff-list { | |
| 113 | + height: 300px; | |
| 114 | + overflow: auto; | |
| 115 | + padding: 10px; | |
| 116 | +} | |
| 117 | + | |
| 118 | +.staff-item { | |
| 119 | + padding: 10px; | |
| 120 | + cursor: pointer; | |
| 121 | + border-bottom: 1px solid #eee; | |
| 122 | +} | |
| 123 | + | |
| 124 | +.staff-item:hover { | |
| 125 | + background-color: #f5f7fa; | |
| 126 | +} | |
| 127 | + | |
| 128 | +.staff-item.selected { | |
| 129 | + background-color: #ecf5ff; | |
| 130 | +} | |
| 131 | + | |
| 132 | +.selected-staff { | |
| 133 | + height: 300px; | |
| 134 | + overflow: auto; | |
| 135 | + padding: 10px; | |
| 136 | +} | |
| 137 | + | |
| 138 | +.selected-item { | |
| 139 | + display: flex; | |
| 140 | + justify-content: space-between; | |
| 141 | + align-items: center; | |
| 142 | + padding: 8px; | |
| 143 | + margin-bottom: 5px; | |
| 144 | + border: 1px solid #ebeef5; | |
| 145 | + border-radius: 4px; | |
| 146 | +} | |
| 147 | + | |
| 148 | +.remove-icon { | |
| 149 | + cursor: pointer; | |
| 150 | + color: #f56c6c; | |
| 151 | +} | |
| 152 | + | |
| 153 | +.text-center { | |
| 154 | + text-align: center; | |
| 155 | + padding: 10px 0; | |
| 156 | +}</style> | |
| 154 | 157 | \ No newline at end of file | ... | ... |
src/i18n/oaI18n.js
| ... | ... | @@ -4,6 +4,12 @@ import { messages as noticeManageMessages } from '../views/oa/noticeManageLang' |
| 4 | 4 | import { messages as addNoticeViewMessages } from '../views/oa/addNoticeViewLang' |
| 5 | 5 | import { messages as editNoticeViewMessages } from '../views/oa/editNoticeViewLang' |
| 6 | 6 | import { messages as noticeDetailMessages } from '../views/oa/noticeDetailLang' |
| 7 | +import { messages as complaintTypeMessages } from '../views/oa/complaintTypeLang' | |
| 8 | +import { messages as addComplaintTypeMessages } from '../views/oa/addComplaintTypeLang' | |
| 9 | +import { messages as editComplaintTypeMessages } from '../views/oa/editComplaintTypeLang' | |
| 10 | +import { messages as addComplaintMessages } from '../views/oa/addComplaintLang' | |
| 11 | +import { messages as complaintMessages } from '../views/oa/complaintLang' | |
| 12 | + | |
| 7 | 13 | export const messages ={ |
| 8 | 14 | en:{ |
| 9 | 15 | ...activitiesTypeManageMessages.en, |
| ... | ... | @@ -12,6 +18,11 @@ export const messages ={ |
| 12 | 18 | ...addNoticeViewMessages.en, |
| 13 | 19 | ...editNoticeViewMessages.en, |
| 14 | 20 | ...noticeDetailMessages.en, |
| 21 | + ...complaintTypeMessages.en, | |
| 22 | + ...addComplaintTypeMessages.en, | |
| 23 | + ...editComplaintTypeMessages.en, | |
| 24 | + ...addComplaintMessages.en, | |
| 25 | + ...complaintMessages.en, | |
| 15 | 26 | }, |
| 16 | 27 | zh:{ |
| 17 | 28 | ...activitiesTypeManageMessages.zh, |
| ... | ... | @@ -20,5 +31,10 @@ export const messages ={ |
| 20 | 31 | ...addNoticeViewMessages.zh, |
| 21 | 32 | ...editNoticeViewMessages.zh, |
| 22 | 33 | ...noticeDetailMessages.zh, |
| 34 | + ...complaintTypeMessages.zh, | |
| 35 | + ...addComplaintTypeMessages.zh, | |
| 36 | + ...editComplaintTypeMessages.zh, | |
| 37 | + ...addComplaintMessages.zh, | |
| 38 | + ...complaintMessages.zh, | |
| 23 | 39 | } |
| 24 | 40 | } |
| 25 | 41 | \ No newline at end of file | ... | ... |
src/router/oaRouter.js
| ... | ... | @@ -29,4 +29,29 @@ export default [ |
| 29 | 29 | name: '/views/oa/noticeDetail', |
| 30 | 30 | component: () => import('@/views/oa/noticeDetailList.vue') |
| 31 | 31 | }, |
| 32 | + { | |
| 33 | + path: '/pages/complaint/complaintType', | |
| 34 | + name: '/pages/complaint/complaintType', | |
| 35 | + component: () => import('@/views/oa/complaintTypeList.vue') | |
| 36 | + }, | |
| 37 | + { | |
| 38 | + path: '/views/oa/addComplaintType', | |
| 39 | + name: '/views/oa/addComplaintType', | |
| 40 | + component: () => import('@/views/oa/addComplaintTypeList.vue') | |
| 41 | + }, | |
| 42 | + { | |
| 43 | + path: '/views/oa/editComplaintType', | |
| 44 | + name: '/views/oa/editComplaintType', | |
| 45 | + component: () => import('@/views/oa/editComplaintTypeList.vue') | |
| 46 | + }, | |
| 47 | + { | |
| 48 | + path: '/views/oa/addComplaint', | |
| 49 | + name: '/views/oa/addComplaint', | |
| 50 | + component: () => import('@/views/oa/addComplaintList.vue') | |
| 51 | + }, | |
| 52 | + { | |
| 53 | + path: '/pages/complaint/complaint', | |
| 54 | + name: '/pages/complaint/complaint', | |
| 55 | + component: () => import('@/views/oa/complaintList.vue') | |
| 56 | + }, | |
| 32 | 57 | ] |
| 33 | 58 | \ No newline at end of file | ... | ... |
src/views/oa/addComplaintLang.js
0 → 100644
| 1 | +export const messages = { | |
| 2 | + en: { | |
| 3 | + addComplaint: { | |
| 4 | + title: 'Register Complaint', | |
| 5 | + type: 'Type', | |
| 6 | + typePlaceholder: 'Required, please select type', | |
| 7 | + room: 'Room', | |
| 8 | + roomPlaceholder: 'Required, please select room', | |
| 9 | + contact: 'Contact Person', | |
| 10 | + contactPlaceholder: 'Required, please enter contact person', | |
| 11 | + phone: 'Phone Number', | |
| 12 | + phonePlaceholder: 'Required, please enter phone number', | |
| 13 | + content: 'Content', | |
| 14 | + contentPlaceholder: 'Required, please enter content', | |
| 15 | + register: 'Register', | |
| 16 | + typeRequired: 'Complaint type is required', | |
| 17 | + contactRequired: 'Contact person is required', | |
| 18 | + contactMaxLength: 'Contact person cannot exceed 200 characters', | |
| 19 | + phoneRequired: 'Phone number is required', | |
| 20 | + phoneFormat: 'Invalid phone number format', | |
| 21 | + contentRequired: 'Content is required', | |
| 22 | + contentMaxLength: 'Content cannot exceed 4000 characters', | |
| 23 | + saveSuccess: 'Complaint registered successfully', | |
| 24 | + saveError: 'Failed to register complaint', | |
| 25 | + fetchTypesError: 'Failed to fetch complaint types' | |
| 26 | + } | |
| 27 | + }, | |
| 28 | + zh: { | |
| 29 | + addComplaint: { | |
| 30 | + title: '登记投诉', | |
| 31 | + type: '类型', | |
| 32 | + typePlaceholder: '必填,请选择类型', | |
| 33 | + room: '房屋', | |
| 34 | + roomPlaceholder: '必填,请选择房屋', | |
| 35 | + contact: '联系人', | |
| 36 | + contactPlaceholder: '必填,请填写联系人', | |
| 37 | + phone: '联系电话', | |
| 38 | + phonePlaceholder: '必填,请填写联系电话', | |
| 39 | + content: '内容', | |
| 40 | + contentPlaceholder: '必填,请填写内容', | |
| 41 | + register: '登记', | |
| 42 | + typeRequired: '投诉类型不能为空', | |
| 43 | + contactRequired: '联系人不能为空', | |
| 44 | + contactMaxLength: '联系人不能大于200位', | |
| 45 | + phoneRequired: '联系电话不能为空', | |
| 46 | + phoneFormat: '联系电话格式错误', | |
| 47 | + contentRequired: '内容不能为空', | |
| 48 | + contentMaxLength: '内容超过4000位', | |
| 49 | + saveSuccess: '添加成功', | |
| 50 | + saveError: '添加失败', | |
| 51 | + fetchTypesError: '获取投诉类型失败' | |
| 52 | + } | |
| 53 | + } | |
| 54 | +} | |
| 0 | 55 | \ No newline at end of file | ... | ... |
src/views/oa/addComplaintList.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="add-complaint-container"> | |
| 3 | + <el-card class="box-card"> | |
| 4 | + <div slot="header" class="clearfix"> | |
| 5 | + <span>{{ $t('addComplaint.title') }}</span> | |
| 6 | + </div> | |
| 7 | + <el-form ref="form" :model="addComplaintInfo" :rules="rules" label-width="120px"> | |
| 8 | + <el-row :gutter="20"> | |
| 9 | + <el-col :span="24"> | |
| 10 | + <el-form-item :label="$t('addComplaint.type')" prop="typeCd"> | |
| 11 | + <el-select v-model="addComplaintInfo.typeCd" :placeholder="$t('addComplaint.typePlaceholder')" | |
| 12 | + style="width:100%"> | |
| 13 | + <el-option v-for="item in addComplaintInfo.complaintTypes" :key="item.typeCd" :label="item.typeName" | |
| 14 | + :value="item.typeCd" /> | |
| 15 | + </el-select> | |
| 16 | + </el-form-item> | |
| 17 | + </el-col> | |
| 18 | + </el-row> | |
| 19 | + | |
| 20 | + <el-row :gutter="20"> | |
| 21 | + <el-col :span="24"> | |
| 22 | + <el-form-item :label="$t('addComplaint.room')" prop="roomName"> | |
| 23 | + <el-input v-model="addComplaintInfo.roomName" :placeholder="$t('addComplaint.roomPlaceholder')" disabled /> | |
| 24 | + <el-button type="primary" @click="selectComplaintRoom" style="margin-left:10px"> | |
| 25 | + {{ $t('addComplaint.register') }} | |
| 26 | + </el-button> | |
| 27 | + </el-form-item> | |
| 28 | + </el-col> | |
| 29 | + </el-row> | |
| 30 | + | |
| 31 | + <el-row :gutter="20"> | |
| 32 | + <el-col :span="12"> | |
| 33 | + <el-form-item :label="$t('addComplaint.contact')" prop="complaintName"> | |
| 34 | + <el-input v-model="addComplaintInfo.complaintName" :placeholder="$t('addComplaint.contactPlaceholder')" /> | |
| 35 | + </el-form-item> | |
| 36 | + </el-col> | |
| 37 | + <el-col :span="12"> | |
| 38 | + <el-form-item :label="$t('addComplaint.phone')" prop="tel"> | |
| 39 | + <el-input v-model="addComplaintInfo.tel" :placeholder="$t('addComplaint.phonePlaceholder')" /> | |
| 40 | + </el-form-item> | |
| 41 | + </el-col> | |
| 42 | + </el-row> | |
| 43 | + | |
| 44 | + <el-row :gutter="20"> | |
| 45 | + <el-col :span="24"> | |
| 46 | + <el-form-item :label="$t('addComplaint.content')" prop="context"> | |
| 47 | + <el-input type="textarea" :rows="4" v-model="addComplaintInfo.context" | |
| 48 | + :placeholder="$t('addComplaint.contentPlaceholder')" /> | |
| 49 | + </el-form-item> | |
| 50 | + </el-col> | |
| 51 | + </el-row> | |
| 52 | + | |
| 53 | + <el-row :gutter="20"> | |
| 54 | + <el-col :span="24" style="text-align:right"> | |
| 55 | + <el-button type="warning" @click="goBack">{{ $t('common.back') }}</el-button> | |
| 56 | + <el-button type="primary" @click="saveComplaint">{{ $t('addComplaint.register') }}</el-button> | |
| 57 | + </el-col> | |
| 58 | + </el-row> | |
| 59 | + </el-form> | |
| 60 | + </el-card> | |
| 61 | + </div> | |
| 62 | +</template> | |
| 63 | + | |
| 64 | +<script> | |
| 65 | +import { listComplaintTypes, saveComplaint } from '@/api/oa/addComplaintApi' | |
| 66 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 67 | + | |
| 68 | +export default { | |
| 69 | + name: 'AddComplaintList', | |
| 70 | + data() { | |
| 71 | + return { | |
| 72 | + addComplaintInfo: { | |
| 73 | + complaintTypes: [], | |
| 74 | + typeCd: '', | |
| 75 | + complaintName: '', | |
| 76 | + tel: '', | |
| 77 | + context: '', | |
| 78 | + communityId: '', | |
| 79 | + roomId: '', | |
| 80 | + roomName: '', | |
| 81 | + ownerId: '', | |
| 82 | + ownerName: '' | |
| 83 | + }, | |
| 84 | + rules: { | |
| 85 | + typeCd: [{ required: true, message: this.$t('addComplaint.typeRequired'), trigger: 'blur' }], | |
| 86 | + complaintName: [ | |
| 87 | + { required: true, message: this.$t('addComplaint.contactRequired'), trigger: 'blur' }, | |
| 88 | + { max: 200, message: this.$t('addComplaint.contactMaxLength'), trigger: 'blur' } | |
| 89 | + ], | |
| 90 | + tel: [ | |
| 91 | + { required: true, message: this.$t('addComplaint.phoneRequired'), trigger: 'blur' }, | |
| 92 | + { pattern: /^1[3-9]\d{9}$/, message: this.$t('addComplaint.phoneFormat'), trigger: 'blur' } | |
| 93 | + ], | |
| 94 | + context: [ | |
| 95 | + { required: true, message: this.$t('addComplaint.contentRequired'), trigger: 'blur' }, | |
| 96 | + { max: 4000, message: this.$t('addComplaint.contentMaxLength'), trigger: 'blur' } | |
| 97 | + ] | |
| 98 | + } | |
| 99 | + } | |
| 100 | + }, | |
| 101 | + created() { | |
| 102 | + this.communityId = getCommunityId() | |
| 103 | + this.listComplaintTypes() | |
| 104 | + }, | |
| 105 | + methods: { | |
| 106 | + async listComplaintTypes() { | |
| 107 | + try { | |
| 108 | + const params = { | |
| 109 | + page: 1, | |
| 110 | + row: 100, | |
| 111 | + communityId: this.communityId | |
| 112 | + } | |
| 113 | + const { data } = await listComplaintTypes(params) | |
| 114 | + this.addComplaintInfo.complaintTypes = data | |
| 115 | + } catch (error) { | |
| 116 | + this.$message.error(this.$t('addComplaint.fetchTypesError')) | |
| 117 | + } | |
| 118 | + }, | |
| 119 | + selectComplaintRoom() { | |
| 120 | + this.$emit('selectRoom') | |
| 121 | + }, | |
| 122 | + async saveComplaint() { | |
| 123 | + await this.$refs.form.validate() | |
| 124 | + | |
| 125 | + try { | |
| 126 | + this.addComplaintInfo.communityId = this.communityId | |
| 127 | + await saveComplaint(this.addComplaintInfo) | |
| 128 | + this.$message.success(this.$t('addComplaint.saveSuccess')) | |
| 129 | + this.goBack() | |
| 130 | + } catch (error) { | |
| 131 | + this.$message.error(error.message || this.$t('addComplaint.saveError')) | |
| 132 | + } | |
| 133 | + }, | |
| 134 | + goBack() { | |
| 135 | + this.$router.go(-1) | |
| 136 | + } | |
| 137 | + } | |
| 138 | +} | |
| 139 | +</script> | |
| 140 | + | |
| 141 | +<style scoped> | |
| 142 | +.add-complaint-container { | |
| 143 | + padding: 20px; | |
| 144 | +} | |
| 145 | + | |
| 146 | +.box-card { | |
| 147 | + margin-bottom: 20px; | |
| 148 | +} | |
| 149 | +</style> | |
| 0 | 150 | \ No newline at end of file | ... | ... |
src/views/oa/addComplaintTypeLang.js
0 → 100644
| 1 | +export const messages = { | |
| 2 | + en: { | |
| 3 | + addComplaintType: { | |
| 4 | + title: 'Add Complaint Type', | |
| 5 | + typeName: 'Type Name', | |
| 6 | + typeNamePlaceholder: 'Required, please enter type name', | |
| 7 | + notifyWay: 'Notification Method', | |
| 8 | + notifyWayPlaceholder: 'Required, please select notification method', | |
| 9 | + appraiseReply: 'Evaluation Reply', | |
| 10 | + appraiseReplyPlaceholder: 'Required, please select evaluation reply', | |
| 11 | + remark: 'Remark', | |
| 12 | + remarkPlaceholder: 'Optional, please enter remark', | |
| 13 | + selectStaff: 'Select Staff', | |
| 14 | + sms: 'SMS', | |
| 15 | + wechat: 'WeChat', | |
| 16 | + workLicense: 'WeChat + Work License', | |
| 17 | + autoReply: 'Auto Reply', | |
| 18 | + manualReply: 'Manual Reply' | |
| 19 | + }, | |
| 20 | + selectStaff: { | |
| 21 | + orgInfo: 'Organization Info', | |
| 22 | + staffInfo: 'Staff Info', | |
| 23 | + selectedStaff: 'Selected Staff', | |
| 24 | + noRepeatSelect: 'Do not select repeatedly' | |
| 25 | + } | |
| 26 | + }, | |
| 27 | + zh: { | |
| 28 | + addComplaintType: { | |
| 29 | + title: '添加投诉类型', | |
| 30 | + typeName: '类型名称', | |
| 31 | + typeNamePlaceholder: '必填,请填写类型名称', | |
| 32 | + notifyWay: '通知方式', | |
| 33 | + notifyWayPlaceholder: '必填,请选择通知方式', | |
| 34 | + appraiseReply: '评价回复', | |
| 35 | + appraiseReplyPlaceholder: '必填,请选择评价回复', | |
| 36 | + remark: '备注', | |
| 37 | + remarkPlaceholder: '选填,请填写备注', | |
| 38 | + selectStaff: '选择员工', | |
| 39 | + sms: '短信', | |
| 40 | + wechat: '微信', | |
| 41 | + workLicense: '微信+员工工牌', | |
| 42 | + autoReply: '自动回复', | |
| 43 | + manualReply: '人工回复' | |
| 44 | + }, | |
| 45 | + selectStaff: { | |
| 46 | + orgInfo: '组织信息', | |
| 47 | + staffInfo: '员工信息', | |
| 48 | + selectedStaff: '已选员工', | |
| 49 | + noRepeatSelect: '请勿重复选择' | |
| 50 | + } | |
| 51 | + } | |
| 52 | +} | |
| 0 | 53 | \ No newline at end of file | ... | ... |
src/views/oa/addComplaintTypeList.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-card class="complaint-type-container"> | |
| 3 | + <div class="header-wrapper"> | |
| 4 | + <h3>{{ $t('addComplaintType.title') }}</h3> | |
| 5 | + </div> | |
| 6 | + | |
| 7 | + <el-form ref="form" :model="addComplaintTypeInfo" label-width="120px"> | |
| 8 | + <el-row :gutter="20"> | |
| 9 | + <el-col :span="12"> | |
| 10 | + <el-form-item :label="$t('addComplaintType.typeName')" prop="typeName"> | |
| 11 | + <el-input v-model="addComplaintTypeInfo.typeName" :placeholder="$t('addComplaintType.typeNamePlaceholder')" | |
| 12 | + clearable /> | |
| 13 | + </el-form-item> | |
| 14 | + </el-col> | |
| 15 | + <el-col :span="12"> | |
| 16 | + <el-form-item :label="$t('addComplaintType.notifyWay')" prop="notifyWay"> | |
| 17 | + <el-select v-model="addComplaintTypeInfo.notifyWay" :placeholder="$t('addComplaintType.notifyWayPlaceholder')" | |
| 18 | + style="width:100%"> | |
| 19 | + <el-option v-for="item in notifyWayOptions" :key="item.value" :label="item.label" :value="item.value" /> | |
| 20 | + </el-select> | |
| 21 | + </el-form-item> | |
| 22 | + </el-col> | |
| 23 | + </el-row> | |
| 24 | + | |
| 25 | + <el-row :gutter="20"> | |
| 26 | + <el-col :span="12"> | |
| 27 | + <el-form-item :label="$t('addComplaintType.appraiseReply')" prop="appraiseReply"> | |
| 28 | + <el-select v-model="addComplaintTypeInfo.appraiseReply" | |
| 29 | + :placeholder="$t('addComplaintType.appraiseReplyPlaceholder')" style="width:100%"> | |
| 30 | + <el-option v-for="item in appraiseReplyOptions" :key="item.value" :label="item.label" :value="item.value" /> | |
| 31 | + </el-select> | |
| 32 | + </el-form-item> | |
| 33 | + </el-col> | |
| 34 | + <el-col :span="12"> | |
| 35 | + <el-form-item :label="$t('addComplaintType.remark')" prop="remark"> | |
| 36 | + <el-input v-model="addComplaintTypeInfo.remark" type="textarea" | |
| 37 | + :placeholder="$t('addComplaintType.remarkPlaceholder')" :rows="2" /> | |
| 38 | + </el-form-item> | |
| 39 | + </el-col> | |
| 40 | + </el-row> | |
| 41 | + | |
| 42 | + <el-row> | |
| 43 | + <el-col :span="24"> | |
| 44 | + <el-form-item :label="$t('addComplaintType.selectStaff')"> | |
| 45 | + <select-staffs ref="selectStaffs" @selectStaffs="handleStaffSelected" /> | |
| 46 | + </el-form-item> | |
| 47 | + </el-col> | |
| 48 | + </el-row> | |
| 49 | + | |
| 50 | + <el-row> | |
| 51 | + <el-col :span="24" class="button-group"> | |
| 52 | + <el-button type="primary" @click="saveComplaintTypeInfo"> | |
| 53 | + {{ $t('common.save') }} | |
| 54 | + </el-button> | |
| 55 | + <el-button @click="goBack"> | |
| 56 | + {{ $t('common.back') }} | |
| 57 | + </el-button> | |
| 58 | + </el-col> | |
| 59 | + </el-row> | |
| 60 | + </el-form> | |
| 61 | + </el-card> | |
| 62 | +</template> | |
| 63 | + | |
| 64 | +<script> | |
| 65 | +import SelectStaffs from '@/components/staff/selectStaffsDiv' | |
| 66 | +import { saveComplaintType } from '@/api/oa/addComplaintTypeApi' | |
| 67 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 68 | + | |
| 69 | +export default { | |
| 70 | + name: 'AddComplaintTypeList', | |
| 71 | + components: { | |
| 72 | + SelectStaffs | |
| 73 | + }, | |
| 74 | + data() { | |
| 75 | + return { | |
| 76 | + addComplaintTypeInfo: { | |
| 77 | + typeName: '', | |
| 78 | + notifyWay: '', | |
| 79 | + appraiseReply: '', | |
| 80 | + remark: '', | |
| 81 | + staffs: [] | |
| 82 | + }, | |
| 83 | + notifyWayOptions: [ | |
| 84 | + { value: 'SMS', label: this.$t('addComplaintType.sms') }, | |
| 85 | + { value: 'WECHAT', label: this.$t('addComplaintType.wechat') }, | |
| 86 | + { value: 'WORK_LICENSE', label: this.$t('addComplaintType.workLicense') } | |
| 87 | + ], | |
| 88 | + appraiseReplyOptions: [ | |
| 89 | + { value: 'Y', label: this.$t('addComplaintType.autoReply') }, | |
| 90 | + { value: 'N', label: this.$t('addComplaintType.manualReply') } | |
| 91 | + ] | |
| 92 | + } | |
| 93 | + }, | |
| 94 | + created() { | |
| 95 | + this.communityId = getCommunityId() | |
| 96 | + }, | |
| 97 | + methods: { | |
| 98 | + validateForm() { | |
| 99 | + return this.$refs.form.validate() | |
| 100 | + }, | |
| 101 | + handleStaffSelected(staffs) { | |
| 102 | + this.addComplaintTypeInfo.staffs = staffs | |
| 103 | + }, | |
| 104 | + async saveComplaintTypeInfo() { | |
| 105 | + try { | |
| 106 | + const isValid = await this.validateForm() | |
| 107 | + if (!isValid) return | |
| 108 | + | |
| 109 | + // 获取选择的员工 | |
| 110 | + this.addComplaintTypeInfo.staffs = this.$refs.selectStaffs.getSelectedStaffs() | |
| 111 | + | |
| 112 | + const params = { | |
| 113 | + ...this.addComplaintTypeInfo, | |
| 114 | + communityId: this.communityId | |
| 115 | + } | |
| 116 | + | |
| 117 | + const res = await saveComplaintType(params) | |
| 118 | + if (res.code === 0) { | |
| 119 | + this.$message.success(this.$t('common.saveSuccess')) | |
| 120 | + this.goBack() | |
| 121 | + } else { | |
| 122 | + this.$message.error(res.msg) | |
| 123 | + } | |
| 124 | + } catch (error) { | |
| 125 | + console.error('保存投诉类型失败:', error) | |
| 126 | + this.$message.error(this.$t('common.saveFailed')) | |
| 127 | + } | |
| 128 | + }, | |
| 129 | + goBack() { | |
| 130 | + this.$router.go(-1) | |
| 131 | + } | |
| 132 | + } | |
| 133 | +} | |
| 134 | +</script> | |
| 135 | + | |
| 136 | +<style lang="scss" scoped> | |
| 137 | +.complaint-type-container { | |
| 138 | + .header-wrapper { | |
| 139 | + margin-bottom: 20px; | |
| 140 | + | |
| 141 | + h3 { | |
| 142 | + font-size: 18px; | |
| 143 | + font-weight: bold; | |
| 144 | + } | |
| 145 | + } | |
| 146 | + | |
| 147 | + .button-group { | |
| 148 | + text-align: right; | |
| 149 | + margin-top: 20px; | |
| 150 | + | |
| 151 | + .el-button { | |
| 152 | + margin-left: 10px; | |
| 153 | + } | |
| 154 | + } | |
| 155 | +} | |
| 156 | +</style> | |
| 0 | 157 | \ No newline at end of file | ... | ... |
src/views/oa/complaintLang.js
0 → 100644
| 1 | +export const messages = { | |
| 2 | + en: { | |
| 3 | + complaint: { | |
| 4 | + search: { | |
| 5 | + title: 'Search Conditions', | |
| 6 | + type: 'Select Type', | |
| 7 | + room: 'Building-Unit-Room e.g. 1-1-1', | |
| 8 | + contact: 'Contact Person', | |
| 9 | + phone: 'Contact Phone', | |
| 10 | + id: 'Complaint ID', | |
| 11 | + startTime: 'Start Time', | |
| 12 | + endTime: 'End Time' | |
| 13 | + }, | |
| 14 | + list: { | |
| 15 | + title: 'Complaints & Suggestions', | |
| 16 | + add: 'Add Complaint' | |
| 17 | + }, | |
| 18 | + status: { | |
| 19 | + all: 'All', | |
| 20 | + processing: 'Processing', | |
| 21 | + completed: 'Completed' | |
| 22 | + }, | |
| 23 | + table: { | |
| 24 | + id: 'ID', | |
| 25 | + type: 'Type', | |
| 26 | + room: 'Room', | |
| 27 | + contact: 'Contact', | |
| 28 | + phone: 'Phone', | |
| 29 | + status: 'Status', | |
| 30 | + handler: 'Handler', | |
| 31 | + createTime: 'Create Time' | |
| 32 | + }, | |
| 33 | + edit: { | |
| 34 | + title: 'Edit Information', | |
| 35 | + type: 'Type', | |
| 36 | + contact: 'Contact', | |
| 37 | + phone: 'Phone', | |
| 38 | + content: 'Content', | |
| 39 | + typePlaceholder: 'Required, please select type', | |
| 40 | + contactPlaceholder: 'Required, please enter contact', | |
| 41 | + phonePlaceholder: 'Required, please enter phone', | |
| 42 | + contentPlaceholder: 'Required, please enter content', | |
| 43 | + success: 'Edit successful' | |
| 44 | + }, | |
| 45 | + delete: { | |
| 46 | + title: 'Confirm Operation', | |
| 47 | + confirm: 'Are you sure to delete this complaint?', | |
| 48 | + success: 'Delete successful' | |
| 49 | + }, | |
| 50 | + detail: { | |
| 51 | + title: 'Details', | |
| 52 | + room: 'Room', | |
| 53 | + id: 'ID', | |
| 54 | + type: 'Type', | |
| 55 | + contact: 'Contact', | |
| 56 | + phone: 'Phone', | |
| 57 | + status: 'Status', | |
| 58 | + handler: 'Handler', | |
| 59 | + handlerPhone: 'Handler Phone', | |
| 60 | + handlerId: 'Handler ID', | |
| 61 | + content: 'Content', | |
| 62 | + photos: 'Photos', | |
| 63 | + serial: 'No.', | |
| 64 | + handleTime: 'Handle Time', | |
| 65 | + duration: 'Duration', | |
| 66 | + comment: 'Comment' | |
| 67 | + }, | |
| 68 | + validate: { | |
| 69 | + type: 'Type is required', | |
| 70 | + contact: 'Contact is required', | |
| 71 | + contactMax: 'Contact cannot exceed 200 characters', | |
| 72 | + phone: 'Phone is required', | |
| 73 | + phoneFormat: 'Invalid phone format', | |
| 74 | + content: 'Content is required', | |
| 75 | + contentMax: 'Content cannot exceed 4000 characters' | |
| 76 | + }, | |
| 77 | + fetchError: 'Failed to fetch complaints data' | |
| 78 | + } | |
| 79 | + }, | |
| 80 | + zh: { | |
| 81 | + complaint: { | |
| 82 | + search: { | |
| 83 | + title: '查询条件', | |
| 84 | + type: '请选择类型', | |
| 85 | + room: '楼栋-单元-房屋 如1-1-1', | |
| 86 | + contact: '请输入联系人', | |
| 87 | + phone: '请输入联系电话', | |
| 88 | + id: '请输入工单编号', | |
| 89 | + startTime: '请选择开始时间', | |
| 90 | + endTime: '请选择结束时间' | |
| 91 | + }, | |
| 92 | + list: { | |
| 93 | + title: '投诉建议', | |
| 94 | + add: '投诉' | |
| 95 | + }, | |
| 96 | + status: { | |
| 97 | + all: '全部', | |
| 98 | + processing: '处理中', | |
| 99 | + completed: '处理完成' | |
| 100 | + }, | |
| 101 | + table: { | |
| 102 | + id: '订单编号', | |
| 103 | + type: '类型', | |
| 104 | + room: '房屋', | |
| 105 | + contact: '联系人', | |
| 106 | + phone: '联系电话', | |
| 107 | + status: '状态', | |
| 108 | + handler: '处理人', | |
| 109 | + createTime: '创建时间' | |
| 110 | + }, | |
| 111 | + edit: { | |
| 112 | + title: '修改信息', | |
| 113 | + type: '类型', | |
| 114 | + contact: '联系人', | |
| 115 | + phone: '联系电话', | |
| 116 | + content: '内容', | |
| 117 | + typePlaceholder: '必填,请选择类型', | |
| 118 | + contactPlaceholder: '必填,请填写联系人', | |
| 119 | + phonePlaceholder: '必填,请填写联系电话', | |
| 120 | + contentPlaceholder: '必填,请填写内容', | |
| 121 | + success: '修改成功' | |
| 122 | + }, | |
| 123 | + delete: { | |
| 124 | + title: '请确认您的操作', | |
| 125 | + confirm: '确定删除投诉建议吗?', | |
| 126 | + success: '删除成功' | |
| 127 | + }, | |
| 128 | + detail: { | |
| 129 | + title: '详情', | |
| 130 | + room: '房屋', | |
| 131 | + id: 'ID', | |
| 132 | + type: '类型', | |
| 133 | + contact: '联系人', | |
| 134 | + phone: '联系电话', | |
| 135 | + status: '状态', | |
| 136 | + handler: '处理人', | |
| 137 | + handlerPhone: '处理人电话', | |
| 138 | + handlerId: '处理人编号', | |
| 139 | + content: '内容', | |
| 140 | + photos: '图片', | |
| 141 | + serial: '序号', | |
| 142 | + handleTime: '处理时间', | |
| 143 | + duration: '耗时', | |
| 144 | + comment: '意见' | |
| 145 | + }, | |
| 146 | + validate: { | |
| 147 | + type: '投诉类型不能为空', | |
| 148 | + contact: '投诉人不能为空', | |
| 149 | + contactMax: '投诉人不能大于200位', | |
| 150 | + phone: '投诉电话不能为空', | |
| 151 | + phoneFormat: '投诉电话格式错误', | |
| 152 | + content: '投诉内容不能为空', | |
| 153 | + contentMax: '投诉状态超过4000位' | |
| 154 | + }, | |
| 155 | + fetchError: '获取投诉数据失败' | |
| 156 | + } | |
| 157 | + } | |
| 158 | +} | |
| 0 | 159 | \ No newline at end of file | ... | ... |
src/views/oa/complaintList.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="complaint-container animated fadeInRight"> | |
| 3 | + <el-row :gutter="20"> | |
| 4 | + <el-col :span="4"> | |
| 5 | + <el-card class="border-radius"> | |
| 6 | + <div class="treeview"> | |
| 7 | + <ul class="list-group text-center border-radius"> | |
| 8 | + <li v-for="(item, index) in complaintInfo.states" :key="index" @click="swatchComplaintState(item)" | |
| 9 | + :class="{ 'vc-node-selected': complaintInfo.conditions.state == item.statusCd }" | |
| 10 | + class="list-group-item node-orgTree"> | |
| 11 | + {{ item.name }} | |
| 12 | + </li> | |
| 13 | + </ul> | |
| 14 | + </div> | |
| 15 | + </el-card> | |
| 16 | + </el-col> | |
| 17 | + <el-col :span="20"> | |
| 18 | + <el-card> | |
| 19 | + <div slot="header" class="flex justify-between"> | |
| 20 | + <span>{{ $t('complaint.search.title') }}</span> | |
| 21 | + </div> | |
| 22 | + <el-row :gutter="20"> | |
| 23 | + <el-col :span="4"> | |
| 24 | + <el-select v-model="complaintInfo.conditions.typeCd" :placeholder="$t('complaint.search.type')" | |
| 25 | + style="width:100%"> | |
| 26 | + <el-option v-for="item in complaintInfo.complaintTypes" :key="item.typeCd" :label="item.typeName" | |
| 27 | + :value="item.typeCd" /> | |
| 28 | + </el-select> | |
| 29 | + </el-col> | |
| 30 | + <el-col :span="4"> | |
| 31 | + <el-input v-model="complaintInfo.conditions.roomName" :placeholder="$t('complaint.search.room')" /> | |
| 32 | + </el-col> | |
| 33 | + <el-col :span="4"> | |
| 34 | + <el-input v-model="complaintInfo.conditions.complaintName" :placeholder="$t('complaint.search.contact')" /> | |
| 35 | + </el-col> | |
| 36 | + <el-col :span="4"> | |
| 37 | + <el-input v-model="complaintInfo.conditions.tel" :placeholder="$t('complaint.search.phone')" /> | |
| 38 | + </el-col> | |
| 39 | + <el-col :span="4"> | |
| 40 | + <el-input v-model="complaintInfo.conditions.complaintId" :placeholder="$t('complaint.search.id')" /> | |
| 41 | + </el-col> | |
| 42 | + <el-col :span="4"> | |
| 43 | + <el-button type="primary" @click="_queryComplaintMethod()"> | |
| 44 | + {{ $t('common.search') }} | |
| 45 | + </el-button> | |
| 46 | + </el-col> | |
| 47 | + </el-row> | |
| 48 | + <el-row :gutter="20" style="margin-top:15px"> | |
| 49 | + <el-col :span="4"> | |
| 50 | + <el-date-picker v-model="complaintInfo.conditions.startTime" type="datetime" | |
| 51 | + :placeholder="$t('complaint.search.startTime')" style="width:100%" /> | |
| 52 | + </el-col> | |
| 53 | + <el-col :span="4"> | |
| 54 | + <el-date-picker v-model="complaintInfo.conditions.endTime" type="datetime" | |
| 55 | + :placeholder="$t('complaint.search.endTime')" style="width:100%" /> | |
| 56 | + </el-col> | |
| 57 | + </el-row> | |
| 58 | + </el-card> | |
| 59 | + | |
| 60 | + <el-card style="margin-top:20px"> | |
| 61 | + <div slot="header" class="flex justify-between"> | |
| 62 | + <span>{{ $t('complaint.list.title') }}</span> | |
| 63 | + <el-button type="primary" size="small" style="float:right" @click="_openAddComplaintModal()"> | |
| 64 | + <i class="el-icon-plus"></i>{{ $t('complaint.list.add') }} | |
| 65 | + </el-button> | |
| 66 | + </div> | |
| 67 | + <el-table :data="complaintInfo.complaints" border style="width:100%" v-loading="loading"> | |
| 68 | + <el-table-column prop="complaintId" :label="$t('complaint.table.id')" align="center" /> | |
| 69 | + <el-table-column prop="typeName" :label="$t('complaint.table.type')" align="center" /> | |
| 70 | + <el-table-column prop="roomName" :label="$t('complaint.table.room')" align="center" /> | |
| 71 | + <el-table-column prop="complaintName" :label="$t('complaint.table.contact')" align="center" /> | |
| 72 | + <el-table-column prop="tel" :label="$t('complaint.table.phone')" align="center" /> | |
| 73 | + <el-table-column prop="stateName" :label="$t('complaint.table.status')" align="center" /> | |
| 74 | + <el-table-column :label="$t('complaint.table.handler')" align="center"> | |
| 75 | + <template slot-scope="scope"> | |
| 76 | + <span v-for="(item, index) in scope.row.staffs" :key="index">{{ item.staffName }}<br /></span> | |
| 77 | + </template> | |
| 78 | + </el-table-column> | |
| 79 | + <el-table-column prop="createTime" :label="$t('complaint.table.createTime')" align="center" /> | |
| 80 | + <el-table-column :label="$t('common.operation')" align="center" width="220"> | |
| 81 | + <template slot-scope="scope"> | |
| 82 | + <el-button-group> | |
| 83 | + <el-button size="mini" @click="_openComplaintDetailModel(scope.row)"> | |
| 84 | + {{ $t('common.detail') }} | |
| 85 | + </el-button> | |
| 86 | + <el-button size="mini" @click="_openEditComplaintModel(scope.row)"> | |
| 87 | + {{ $t('common.edit') }} | |
| 88 | + </el-button> | |
| 89 | + <el-button size="mini" type="danger" @click="_openDeleteComplaintModel(scope.row)"> | |
| 90 | + {{ $t('common.delete') }} | |
| 91 | + </el-button> | |
| 92 | + </el-button-group> | |
| 93 | + </template> | |
| 94 | + </el-table-column> | |
| 95 | + </el-table> | |
| 96 | + <el-pagination style="margin-top:15px" @size-change="handleSizeChange" @current-change="handleCurrentChange" | |
| 97 | + :current-page="page.current" :page-sizes="[10, 20, 30, 50]" :page-size="page.size" | |
| 98 | + layout="total, sizes, prev, pager, next, jumper" :total="page.total" /> | |
| 99 | + </el-card> | |
| 100 | + </el-col> | |
| 101 | + </el-row> | |
| 102 | + | |
| 103 | + <view-image ref="viewImage" /> | |
| 104 | + <edit-complaint ref="editComplaint" @success="handleSuccess" /> | |
| 105 | + <delete-complaint ref="deleteComplaint" @success="handleSuccess" /> | |
| 106 | + <complaint-detail ref="complaintDetail" /> | |
| 107 | + </div> | |
| 108 | +</template> | |
| 109 | + | |
| 110 | +<script> | |
| 111 | +import { listComplaints, listComplaintTypes } from '@/api/oa/complaintApi' | |
| 112 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 113 | +import ViewImage from '@/components/system/viewImage' | |
| 114 | +import EditComplaint from '@/components/oa/editComplaint' | |
| 115 | +import DeleteComplaint from '@/components/oa/deleteComplaint' | |
| 116 | +import ComplaintDetail from '@/components/oa/complaintDetail' | |
| 117 | + | |
| 118 | +export default { | |
| 119 | + name: 'ComplaintList', | |
| 120 | + components: { | |
| 121 | + ViewImage, | |
| 122 | + EditComplaint, | |
| 123 | + DeleteComplaint, | |
| 124 | + ComplaintDetail | |
| 125 | + }, | |
| 126 | + data() { | |
| 127 | + return { | |
| 128 | + loading: false, | |
| 129 | + page: { | |
| 130 | + current: 1, | |
| 131 | + size: 10, | |
| 132 | + total: 0 | |
| 133 | + }, | |
| 134 | + complaintInfo: { | |
| 135 | + complaints: [], | |
| 136 | + states: [ | |
| 137 | + { statusCd: '', name: this.$t('complaint.status.all') }, | |
| 138 | + { statusCd: 'W', name: this.$t('complaint.status.processing') }, | |
| 139 | + { statusCd: 'C', name: this.$t('complaint.status.completed') } | |
| 140 | + ], | |
| 141 | + complaintTypes: [], | |
| 142 | + conditions: { | |
| 143 | + complaintId: '', | |
| 144 | + typeCd: '', | |
| 145 | + complaintName: '', | |
| 146 | + tel: '', | |
| 147 | + roomName: '', | |
| 148 | + state: '', | |
| 149 | + startTime: '', | |
| 150 | + endTime: '', | |
| 151 | + communityId: '' | |
| 152 | + } | |
| 153 | + } | |
| 154 | + } | |
| 155 | + }, | |
| 156 | + created() { | |
| 157 | + this.communityId = getCommunityId() | |
| 158 | + this.complaintInfo.conditions.communityId = this.communityId | |
| 159 | + this._listComplaintTypes() | |
| 160 | + this._listComplaints() | |
| 161 | + }, | |
| 162 | + methods: { | |
| 163 | + async _listComplaints() { | |
| 164 | + try { | |
| 165 | + this.loading = true | |
| 166 | + const params = { | |
| 167 | + ...this.complaintInfo.conditions, | |
| 168 | + page: this.page.current, | |
| 169 | + row: this.page.size | |
| 170 | + } | |
| 171 | + const { data, total } = await listComplaints(params) | |
| 172 | + this.complaintInfo.complaints = data | |
| 173 | + this.page.total = total | |
| 174 | + } catch (error) { | |
| 175 | + this.$message.error(this.$t('complaint.fetchError')) | |
| 176 | + } finally { | |
| 177 | + this.loading = false | |
| 178 | + } | |
| 179 | + }, | |
| 180 | + async _listComplaintTypes() { | |
| 181 | + try { | |
| 182 | + const params = { | |
| 183 | + page: 1, | |
| 184 | + row: 100, | |
| 185 | + communityId: this.communityId | |
| 186 | + } | |
| 187 | + const { data } = await listComplaintTypes(params) | |
| 188 | + this.complaintInfo.complaintTypes = data | |
| 189 | + } catch (error) { | |
| 190 | + console.error('获取投诉类型失败:', error) | |
| 191 | + } | |
| 192 | + }, | |
| 193 | + swatchComplaintState(item) { | |
| 194 | + this.complaintInfo.conditions.state = item.statusCd | |
| 195 | + this._listComplaints() | |
| 196 | + }, | |
| 197 | + _openAddComplaintModal() { | |
| 198 | + this.$router.push('/views/oa/addComplaint') | |
| 199 | + }, | |
| 200 | + _openEditComplaintModel(row) { | |
| 201 | + this.$refs.editComplaint.open(row) | |
| 202 | + }, | |
| 203 | + _openDeleteComplaintModel(row) { | |
| 204 | + this.$refs.deleteComplaint.open(row) | |
| 205 | + }, | |
| 206 | + _openComplaintDetailModel(row) { | |
| 207 | + this.$refs.complaintDetail.open(row) | |
| 208 | + }, | |
| 209 | + _queryComplaintMethod() { | |
| 210 | + this.page.current = 1 | |
| 211 | + this._listComplaints() | |
| 212 | + }, | |
| 213 | + handleSizeChange(val) { | |
| 214 | + this.page.size = val | |
| 215 | + this._listComplaints() | |
| 216 | + }, | |
| 217 | + handleCurrentChange(val) { | |
| 218 | + this.page.current = val | |
| 219 | + this._listComplaints() | |
| 220 | + }, | |
| 221 | + handleSuccess() { | |
| 222 | + this._listComplaints() | |
| 223 | + } | |
| 224 | + } | |
| 225 | +} | |
| 226 | +</script> | |
| 227 | + | |
| 228 | +<style lang="scss" scoped> | |
| 229 | +.complaint-container { | |
| 230 | + padding: 20px; | |
| 231 | + | |
| 232 | + .border-radius { | |
| 233 | + border-radius: 4px; | |
| 234 | + } | |
| 235 | + | |
| 236 | + .treeview { | |
| 237 | + .list-group { | |
| 238 | + padding: 0; | |
| 239 | + margin: 0; | |
| 240 | + list-style: none; | |
| 241 | + | |
| 242 | + .list-group-item { | |
| 243 | + padding: 10px 15px; | |
| 244 | + margin-bottom: -1px; | |
| 245 | + border: 1px solid #ddd; | |
| 246 | + cursor: pointer; | |
| 247 | + | |
| 248 | + &:hover { | |
| 249 | + background-color: #f5f5f5; | |
| 250 | + } | |
| 251 | + | |
| 252 | + &.vc-node-selected { | |
| 253 | + background-color: #409EFF; | |
| 254 | + color: #fff; | |
| 255 | + } | |
| 256 | + } | |
| 257 | + } | |
| 258 | + } | |
| 259 | +} | |
| 260 | +</style> | |
| 0 | 261 | \ No newline at end of file | ... | ... |
src/views/oa/complaintTypeLang.js
0 → 100644
| 1 | +export const messages = { | |
| 2 | + en: { | |
| 3 | + complaintType: { | |
| 4 | + title: 'Complaint Type', | |
| 5 | + add: 'Add', | |
| 6 | + typeName: 'Type Name', | |
| 7 | + notifyWay: 'Notification Method', | |
| 8 | + sms: 'SMS', | |
| 9 | + wechat: 'WeChat', | |
| 10 | + wechatWorkLicense: 'WeChat + Work License', | |
| 11 | + appraiseReply: 'Evaluation Reply', | |
| 12 | + autoReply: 'Auto Reply', | |
| 13 | + manualReply: 'Manual Reply', | |
| 14 | + handler: 'Handler', | |
| 15 | + createTime: 'Create Time', | |
| 16 | + fetchError: 'Failed to fetch complaint types' | |
| 17 | + }, | |
| 18 | + deleteComplaintType: { | |
| 19 | + confirmTitle: 'Confirm Operation', | |
| 20 | + confirmText: 'Are you sure to delete this complaint type?', | |
| 21 | + confirm: 'Confirm Delete', | |
| 22 | + cancel: 'Cancel', | |
| 23 | + success: 'Delete successfully', | |
| 24 | + error: 'Delete failed' | |
| 25 | + } | |
| 26 | + }, | |
| 27 | + zh: { | |
| 28 | + complaintType: { | |
| 29 | + title: '投诉类型', | |
| 30 | + add: '添加', | |
| 31 | + typeName: '类型名称', | |
| 32 | + notifyWay: '通知方式', | |
| 33 | + sms: '短信', | |
| 34 | + wechat: '微信', | |
| 35 | + wechatWorkLicense: '微信+员工工牌', | |
| 36 | + appraiseReply: '评价回复', | |
| 37 | + autoReply: '自动回复', | |
| 38 | + manualReply: '人工回复', | |
| 39 | + handler: '处理人', | |
| 40 | + createTime: '创建时间', | |
| 41 | + fetchError: '获取投诉类型失败' | |
| 42 | + }, | |
| 43 | + deleteComplaintType: { | |
| 44 | + confirmTitle: '请确认您的操作', | |
| 45 | + confirmText: '确定删除投诉类型', | |
| 46 | + confirm: '确认删除', | |
| 47 | + cancel: '点错了', | |
| 48 | + success: '删除成功', | |
| 49 | + error: '删除失败' | |
| 50 | + } | |
| 51 | + } | |
| 52 | +} | |
| 0 | 53 | \ No newline at end of file | ... | ... |
src/views/oa/complaintTypeList.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="complaint-type-container"> | |
| 3 | + <el-card class="box-card"> | |
| 4 | + <div slot="header" class="flex justify-between"> | |
| 5 | + <span>{{ $t('complaintType.title') }}</span> | |
| 6 | + <el-button type="primary" size="small" style="float: right;" @click="_openAddComplaintTypeModal"> | |
| 7 | + {{ $t('complaintType.add') }} | |
| 8 | + </el-button> | |
| 9 | + </div> | |
| 10 | + | |
| 11 | + <el-table :data="complaintTypeInfo.complaintTypes" border style="width: 100%" v-loading="loading"> | |
| 12 | + <el-table-column prop="typeName" :label="$t('complaintType.typeName')" align="center" /> | |
| 13 | + <el-table-column prop="notifyWay" :label="$t('complaintType.notifyWay')" align="center"> | |
| 14 | + <template slot-scope="scope"> | |
| 15 | + <span v-if="scope.row.notifyWay === 'SMS'"> | |
| 16 | + {{ $t('complaintType.sms') }} | |
| 17 | + </span> | |
| 18 | + <span v-else-if="scope.row.notifyWay === 'WORK_LICENSE'"> | |
| 19 | + {{ $t('complaintType.wechatWorkLicense') }} | |
| 20 | + </span> | |
| 21 | + <span v-else> | |
| 22 | + {{ $t('complaintType.wechat') }} | |
| 23 | + </span> | |
| 24 | + </template> | |
| 25 | + </el-table-column> | |
| 26 | + <el-table-column prop="appraiseReply" :label="$t('complaintType.appraiseReply')" align="center"> | |
| 27 | + <template slot-scope="scope"> | |
| 28 | + {{ scope.row.appraiseReply === 'Y' ? $t('complaintType.autoReply') : $t('complaintType.manualReply') }} | |
| 29 | + </template> | |
| 30 | + </el-table-column> | |
| 31 | + <el-table-column prop="staffs" :label="$t('complaintType.handler')" align="center"> | |
| 32 | + <template slot-scope="scope"> | |
| 33 | + <div v-for="(item, index) in scope.row.staffs" :key="index"> | |
| 34 | + {{ item.staffName }} | |
| 35 | + </div> | |
| 36 | + </template> | |
| 37 | + </el-table-column> | |
| 38 | + <el-table-column prop="createTime" :label="$t('complaintType.createTime')" align="center" /> | |
| 39 | + <el-table-column :label="$t('common.operation')" align="center" width="200"> | |
| 40 | + <template slot-scope="scope"> | |
| 41 | + <el-button size="mini" type="primary" @click="_openEditComplaintTypeModel(scope.row)"> | |
| 42 | + {{ $t('common.edit') }} | |
| 43 | + </el-button> | |
| 44 | + <el-button size="mini" type="danger" @click="_openDeleteComplaintTypeModel(scope.row)"> | |
| 45 | + {{ $t('common.delete') }} | |
| 46 | + </el-button> | |
| 47 | + </template> | |
| 48 | + </el-table-column> | |
| 49 | + </el-table> | |
| 50 | + | |
| 51 | + <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="page.current" | |
| 52 | + :page-sizes="[10, 20, 30, 50]" :page-size="page.size" layout="total, sizes, prev, pager, next, jumper" | |
| 53 | + :total="complaintTypeInfo.total" style="margin-top: 20px;" /> | |
| 54 | + </el-card> | |
| 55 | + | |
| 56 | + <delete-complaint-type ref="deleteComplaintType" @success="handleSuccess" /> | |
| 57 | + </div> | |
| 58 | +</template> | |
| 59 | + | |
| 60 | +<script> | |
| 61 | +import { listComplaintType } from '@/api/oa/complaintTypeApi' | |
| 62 | +import DeleteComplaintType from '@/components/oa/deleteComplaintType' | |
| 63 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 64 | + | |
| 65 | +export default { | |
| 66 | + name: 'ComplaintTypeList', | |
| 67 | + components: { | |
| 68 | + DeleteComplaintType | |
| 69 | + }, | |
| 70 | + data() { | |
| 71 | + return { | |
| 72 | + loading: false, | |
| 73 | + complaintTypeInfo: { | |
| 74 | + complaintTypes: [], | |
| 75 | + total: 0, | |
| 76 | + conditions: { | |
| 77 | + typeCd: '', | |
| 78 | + typeName: '', | |
| 79 | + notifyWay: '' | |
| 80 | + } | |
| 81 | + }, | |
| 82 | + page: { | |
| 83 | + current: 1, | |
| 84 | + size: 10 | |
| 85 | + } | |
| 86 | + } | |
| 87 | + }, | |
| 88 | + created() { | |
| 89 | + this.communityId = getCommunityId() | |
| 90 | + this._listComplaintTypes() | |
| 91 | + }, | |
| 92 | + methods: { | |
| 93 | + async _listComplaintTypes() { | |
| 94 | + try { | |
| 95 | + this.loading = true | |
| 96 | + const params = { | |
| 97 | + page: this.page.current, | |
| 98 | + row: this.page.size, | |
| 99 | + communityId: this.communityId, | |
| 100 | + ...this.complaintTypeInfo.conditions | |
| 101 | + } | |
| 102 | + const { data, total } = await listComplaintType(params) | |
| 103 | + this.complaintTypeInfo.complaintTypes = data | |
| 104 | + this.complaintTypeInfo.total = total | |
| 105 | + } catch (error) { | |
| 106 | + this.$message.error(this.$t('complaintType.fetchError')) | |
| 107 | + } finally { | |
| 108 | + this.loading = false | |
| 109 | + } | |
| 110 | + }, | |
| 111 | + _openAddComplaintTypeModal() { | |
| 112 | + this.$router.push('/views/oa/addComplaintType') | |
| 113 | + }, | |
| 114 | + _openEditComplaintTypeModel(complaintType) { | |
| 115 | + this.$router.push(`/views/oa/editComplaintType?typeCd=${complaintType.typeCd}`) | |
| 116 | + }, | |
| 117 | + _openDeleteComplaintTypeModel(complaintType) { | |
| 118 | + this.$refs.deleteComplaintType.open(complaintType) | |
| 119 | + }, | |
| 120 | + handleSuccess() { | |
| 121 | + this._listComplaintTypes() | |
| 122 | + }, | |
| 123 | + handleSizeChange(val) { | |
| 124 | + this.page.size = val | |
| 125 | + this._listComplaintTypes() | |
| 126 | + }, | |
| 127 | + handleCurrentChange(val) { | |
| 128 | + this.page.current = val | |
| 129 | + this._listComplaintTypes() | |
| 130 | + } | |
| 131 | + } | |
| 132 | +} | |
| 133 | +</script> | |
| 134 | + | |
| 135 | +<style lang="scss" scoped> | |
| 136 | +.complaint-type-container { | |
| 137 | + padding: 20px; | |
| 138 | + | |
| 139 | + .box-card { | |
| 140 | + margin-bottom: 20px; | |
| 141 | + } | |
| 142 | + | |
| 143 | + .el-pagination { | |
| 144 | + margin-top: 20px; | |
| 145 | + text-align: right; | |
| 146 | + } | |
| 147 | +} | |
| 148 | +</style> | |
| 0 | 149 | \ No newline at end of file | ... | ... |
src/views/oa/editComplaintTypeLang.js
0 → 100644
| 1 | +export const messages = { | |
| 2 | + en: { | |
| 3 | + editComplaintType: { | |
| 4 | + title: 'Edit Complaint Type', | |
| 5 | + typeName: 'Type Name', | |
| 6 | + typeNamePlaceholder: 'Required, please enter type name', | |
| 7 | + notifyWay: 'Notification Method', | |
| 8 | + notifyWayPlaceholder: 'Required, please select notification method', | |
| 9 | + sms: 'SMS', | |
| 10 | + wechat: 'WeChat', | |
| 11 | + workLicense: 'WeChat + Work License', | |
| 12 | + appraiseReply: 'Evaluation Reply', | |
| 13 | + appraiseReplyPlaceholder: 'Required, please select evaluation reply', | |
| 14 | + autoReply: 'Auto Reply', | |
| 15 | + manualReply: 'Manual Reply', | |
| 16 | + remark: 'Remark', | |
| 17 | + remarkPlaceholder: 'Optional, please enter remark', | |
| 18 | + selectStaff: 'Select Staff', | |
| 19 | + saveSuccess: 'Save successfully', | |
| 20 | + saveError: 'Save failed', | |
| 21 | + loadError: 'Failed to load complaint type details' | |
| 22 | + }, | |
| 23 | + selectStaff: { | |
| 24 | + orgInfo: 'Organization Information', | |
| 25 | + staffInfo: 'Staff Information', | |
| 26 | + selectedStaff: 'Selected Staff', | |
| 27 | + staffAlreadySelected: 'Staff already selected', | |
| 28 | + loadStaffError: 'Failed to load staff information' | |
| 29 | + }, | |
| 30 | + orgTree: { | |
| 31 | + loadError: 'Failed to load organization tree' | |
| 32 | + } | |
| 33 | + }, | |
| 34 | + zh: { | |
| 35 | + editComplaintType: { | |
| 36 | + title: '修改投诉类型', | |
| 37 | + typeName: '类型名称', | |
| 38 | + typeNamePlaceholder: '必填,请填写类型名称', | |
| 39 | + notifyWay: '通知方式', | |
| 40 | + notifyWayPlaceholder: '必填,请选择通知方式', | |
| 41 | + sms: '短信', | |
| 42 | + wechat: '微信', | |
| 43 | + workLicense: '微信+员工工牌', | |
| 44 | + appraiseReply: '评价回复', | |
| 45 | + appraiseReplyPlaceholder: '必填,请选择评价回复', | |
| 46 | + autoReply: '自动回复', | |
| 47 | + manualReply: '人工回复', | |
| 48 | + remark: '备注', | |
| 49 | + remarkPlaceholder: '选填,请填写备注', | |
| 50 | + selectStaff: '选择员工', | |
| 51 | + saveSuccess: '保存成功', | |
| 52 | + saveError: '保存失败', | |
| 53 | + loadError: '加载投诉类型详情失败' | |
| 54 | + }, | |
| 55 | + selectStaff: { | |
| 56 | + orgInfo: '组织信息', | |
| 57 | + staffInfo: '员工信息', | |
| 58 | + selectedStaff: '已选员工', | |
| 59 | + staffAlreadySelected: '请勿重复选择', | |
| 60 | + loadStaffError: '加载员工信息失败' | |
| 61 | + }, | |
| 62 | + orgTree: { | |
| 63 | + loadError: '加载组织树失败' | |
| 64 | + } | |
| 65 | + } | |
| 66 | +} | |
| 0 | 67 | \ No newline at end of file | ... | ... |
src/views/oa/editComplaintTypeList.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-card class="edit-complaint-type-container"> | |
| 3 | + <div slot="header" class="clearfix"> | |
| 4 | + <span>{{ $t('editComplaintType.title') }}</span> | |
| 5 | + </div> | |
| 6 | + | |
| 7 | + <el-form ref="form" :model="editComplaintTypeInfo" label-width="120px"> | |
| 8 | + <el-row :gutter="20"> | |
| 9 | + <el-col :span="12"> | |
| 10 | + <el-form-item :label="$t('editComplaintType.typeName')" prop="typeName"> | |
| 11 | + <el-input v-model="editComplaintTypeInfo.typeName" | |
| 12 | + :placeholder="$t('editComplaintType.typeNamePlaceholder')" /> | |
| 13 | + </el-form-item> | |
| 14 | + </el-col> | |
| 15 | + <el-col :span="12"> | |
| 16 | + <el-form-item :label="$t('editComplaintType.notifyWay')" prop="notifyWay"> | |
| 17 | + <el-select v-model="editComplaintTypeInfo.notifyWay" | |
| 18 | + :placeholder="$t('editComplaintType.notifyWayPlaceholder')" style="width:100%"> | |
| 19 | + <el-option v-for="item in notifyWayOptions" :key="item.value" :label="item.label" :value="item.value" /> | |
| 20 | + </el-select> | |
| 21 | + </el-form-item> | |
| 22 | + </el-col> | |
| 23 | + </el-row> | |
| 24 | + | |
| 25 | + <el-row :gutter="20"> | |
| 26 | + <el-col :span="12"> | |
| 27 | + <el-form-item :label="$t('editComplaintType.appraiseReply')" prop="appraiseReply"> | |
| 28 | + <el-select v-model="editComplaintTypeInfo.appraiseReply" | |
| 29 | + :placeholder="$t('editComplaintType.appraiseReplyPlaceholder')" style="width:100%"> | |
| 30 | + <el-option v-for="item in appraiseReplyOptions" :key="item.value" :label="item.label" :value="item.value" /> | |
| 31 | + </el-select> | |
| 32 | + </el-form-item> | |
| 33 | + </el-col> | |
| 34 | + <el-col :span="12"> | |
| 35 | + <el-form-item :label="$t('editComplaintType.remark')" prop="remark"> | |
| 36 | + <el-input v-model="editComplaintTypeInfo.remark" type="textarea" | |
| 37 | + :placeholder="$t('editComplaintType.remarkPlaceholder')" /> | |
| 38 | + </el-form-item> | |
| 39 | + </el-col> | |
| 40 | + </el-row> | |
| 41 | + | |
| 42 | + <el-row> | |
| 43 | + <el-col :span="24"> | |
| 44 | + <el-form-item :label="$t('editComplaintType.selectStaff')"> | |
| 45 | + <select-staffs ref="selectStaffs" @selectStaffs="handleStaffSelected"/> | |
| 46 | + </el-form-item> | |
| 47 | + </el-col> | |
| 48 | + </el-row> | |
| 49 | + | |
| 50 | + <el-row> | |
| 51 | + <el-col :span="24" class="text-right"> | |
| 52 | + <el-button type="warning" @click="handleBack"> | |
| 53 | + <i class="el-icon-close"></i> | |
| 54 | + {{ $t('common.back') }} | |
| 55 | + </el-button> | |
| 56 | + <el-button type="primary" @click="handleSave"> | |
| 57 | + <i class="el-icon-check"></i> | |
| 58 | + {{ $t('common.save') }} | |
| 59 | + </el-button> | |
| 60 | + </el-col> | |
| 61 | + </el-row> | |
| 62 | + </el-form> | |
| 63 | + </el-card> | |
| 64 | +</template> | |
| 65 | + | |
| 66 | +<script> | |
| 67 | +import { updateComplaintType, getComplaintTypeDetail } from '@/api/oa/editComplaintTypeApi' | |
| 68 | +import SelectStaffs from '@/components/staff/selectStaffsDiv' | |
| 69 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 70 | + | |
| 71 | +export default { | |
| 72 | + name: 'EditComplaintTypeList', | |
| 73 | + components: { | |
| 74 | + SelectStaffs | |
| 75 | + }, | |
| 76 | + data() { | |
| 77 | + return { | |
| 78 | + editComplaintTypeInfo: { | |
| 79 | + typeCd: '', | |
| 80 | + typeName: '', | |
| 81 | + notifyWay: '', | |
| 82 | + appraiseReply: '', | |
| 83 | + remark: '', | |
| 84 | + staffs: [], | |
| 85 | + communityId: '' | |
| 86 | + }, | |
| 87 | + notifyWayOptions: [ | |
| 88 | + { value: 'SMS', label: this.$t('editComplaintType.sms') }, | |
| 89 | + { value: 'WECHAT', label: this.$t('editComplaintType.wechat') }, | |
| 90 | + { value: 'WORK_LICENSE', label: this.$t('editComplaintType.workLicense') } | |
| 91 | + ], | |
| 92 | + appraiseReplyOptions: [ | |
| 93 | + { value: 'Y', label: this.$t('editComplaintType.autoReply') }, | |
| 94 | + { value: 'N', label: this.$t('editComplaintType.manualReply') } | |
| 95 | + ] | |
| 96 | + } | |
| 97 | + }, | |
| 98 | + created() { | |
| 99 | + this.editComplaintTypeInfo.communityId = getCommunityId() | |
| 100 | + this.editComplaintTypeInfo.typeCd = this.$route.query.typeCd | |
| 101 | + this.loadComplaintTypeDetail() | |
| 102 | + }, | |
| 103 | + methods: { | |
| 104 | + handleStaffSelected(staffs) { | |
| 105 | + this.editComplaintTypeInfo.staffs = staffs | |
| 106 | + }, | |
| 107 | + async loadComplaintTypeDetail() { | |
| 108 | + try { | |
| 109 | + const params = { | |
| 110 | + typeCd: this.editComplaintTypeInfo.typeCd, | |
| 111 | + communityId: this.editComplaintTypeInfo.communityId, | |
| 112 | + page: 1, | |
| 113 | + row: 1 | |
| 114 | + } | |
| 115 | + const { data } = await getComplaintTypeDetail(params) | |
| 116 | + if (data && data.length > 0) { | |
| 117 | + this.editComplaintTypeInfo = { ...data[0] } | |
| 118 | + let _staffs = [] | |
| 119 | + data[0].staffs.forEach(staff => { | |
| 120 | + _staffs.push({ userId: staff.staffId, name: staff.staffName }) | |
| 121 | + }); | |
| 122 | + this.$refs.selectStaffs.setStaffs(_staffs || []) | |
| 123 | + } | |
| 124 | + } catch (error) { | |
| 125 | + this.$message.error(this.$t('editComplaintType.loadError')) | |
| 126 | + } | |
| 127 | + }, | |
| 128 | + validateForm() { | |
| 129 | + return this.$refs.form.validate() | |
| 130 | + }, | |
| 131 | + async handleSave() { | |
| 132 | + try { | |
| 133 | + const isValid = await this.validateForm() | |
| 134 | + if (!isValid) return | |
| 135 | + | |
| 136 | + this.editComplaintTypeInfo.staffs = this.$refs.selectStaffs.getSelectedStaffs() | |
| 137 | + await updateComplaintType(this.editComplaintTypeInfo) | |
| 138 | + this.$message.success(this.$t('editComplaintType.saveSuccess')) | |
| 139 | + this.handleBack() | |
| 140 | + } catch (error) { | |
| 141 | + this.$message.error(error.message || this.$t('editComplaintType.saveError')) | |
| 142 | + } | |
| 143 | + }, | |
| 144 | + handleBack() { | |
| 145 | + this.$router.go(-1) | |
| 146 | + } | |
| 147 | + } | |
| 148 | +} | |
| 149 | +</script> | |
| 150 | + | |
| 151 | +<style lang="scss" scoped> | |
| 152 | +.edit-complaint-type-container { | |
| 153 | + margin: 20px; | |
| 154 | + | |
| 155 | + .text-right { | |
| 156 | + text-align: right; | |
| 157 | + } | |
| 158 | + | |
| 159 | + .el-form-item { | |
| 160 | + margin-bottom: 22px; | |
| 161 | + } | |
| 162 | +} | |
| 163 | +</style> | |
| 0 | 164 | \ No newline at end of file | ... | ... |