Commit 03f63ab425d41ad23f1bf8064bf3c5a84e8b17dd

Authored by wuxw
1 parent 8592fee7

优化到商户信息

Showing 42 changed files with 3832 additions and 89 deletions
src/api/org/addScheduleClassesApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +/**
  4 + * 保存排班信息
  5 + * @param {Object} data 排班数据
  6 + * @returns {Promise}
  7 + */
  8 +export function saveScheduleClasses(data) {
  9 + return new Promise((resolve, reject) => {
  10 + request({
  11 + url: '/scheduleClasses.saveScheduleClasses',
  12 + method: 'post',
  13 + data
  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 listClasses(params) {
  29 + return new Promise((resolve, reject) => {
  30 + request({
  31 + url: '/classes.listClasses',
  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 {String} scheduleId 排班ID
  46 + * @returns {Promise}
  47 + */
  48 +export function getScheduleClassesDetail(scheduleId) {
  49 + return new Promise((resolve, reject) => {
  50 + request({
  51 + url: '/scheduleClasses.getScheduleClasses',
  52 + method: 'get',
  53 + params: { scheduleId }
  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/org/editScheduleClassesApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 更新排班信息
  4 +export function updateScheduleClasses(data) {
  5 + return new Promise((resolve, reject) => {
  6 + request({
  7 + url: '/scheduleClasses.updateScheduleClasses',
  8 + method: 'post',
  9 + data
  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 listScheduleClasses(params) {
  21 + return new Promise((resolve, reject) => {
  22 + request({
  23 + url: '/scheduleClasses.listScheduleClasses',
  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 listScheduleClassesDay(params) {
  37 + return new Promise((resolve, reject) => {
  38 + request({
  39 + url: '/scheduleClasses.listScheduleClassesDay',
  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 listClasses(params) {
  53 + return new Promise((resolve, reject) => {
  54 + request({
  55 + url: '/classes.listClasses',
  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 +}
0 66 \ No newline at end of file
... ...
src/api/org/scheduleClassesApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +// 获取排班列表
  5 +export function listScheduleClasses(params) {
  6 + return new Promise((resolve, reject) => {
  7 + request({
  8 + url: '/scheduleClasses.listScheduleClasses',
  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 deleteScheduleClasses(data) {
  25 + return new Promise((resolve, reject) => {
  26 + request({
  27 + url: '/scheduleClasses.deleteScheduleClasses',
  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 updateScheduleClassesState(data) {
  44 + return new Promise((resolve, reject) => {
  45 + request({
  46 + url: '/scheduleClasses.updateScheduleClassesState',
  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 getDict(dictType, dictName) {
  63 + return new Promise((resolve, reject) => {
  64 + request({
  65 + url: '/dict.getDict',
  66 + method: 'get',
  67 + params: {
  68 + dictType,
  69 + dictName,
  70 + communityId: getCommunityId()
  71 + }
  72 + }).then(response => {
  73 + const res = response.data
  74 + resolve(res)
  75 + }).catch(error => {
  76 + reject(error)
  77 + })
  78 + })
  79 +}
0 80 \ No newline at end of file
... ...
src/api/org/scheduleClassesPageApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +/**
  4 + * 获取班次列表
  5 + * @param {Object} params 查询参数
  6 + * @returns {Promise} Promise对象
  7 + */
  8 +export function listScheduleClasses(params) {
  9 + return new Promise((resolve, reject) => {
  10 + request({
  11 + url: '/scheduleClasses.listScheduleClasses',
  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} Promise对象
  27 + */
  28 +export function staffMonthScheduleClasses(params) {
  29 + return new Promise((resolve, reject) => {
  30 + request({
  31 + url: '/scheduleClasses.staffMonthScheduleClasses',
  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} params 导出参数
  46 + * @returns {Promise} Promise对象
  47 + */
  48 +export function exportData(params) {
  49 + return new Promise((resolve, reject) => {
  50 + request({
  51 + url: '/export.exportData',
  52 + method: 'get',
  53 + params
  54 + }).then(response => {
  55 + const res = response.data
  56 + resolve(res)
  57 + }).catch(error => {
  58 + reject(error)
  59 + })
  60 + })
  61 +}
  62 +
  63 +/**
  64 + * 获取组织树
  65 + * @param {Object} params 查询参数
  66 + * @returns {Promise} Promise对象
  67 + */
  68 +export function listOrgTree(params) {
  69 + return new Promise((resolve, reject) => {
  70 + request({
  71 + url: '/org.listOrgTree',
  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 +}
0 82 \ No newline at end of file
... ...
src/api/system/communitySettingManageApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +/**
  4 + * 获取小区设置项列表
  5 + * @param {Object} params 查询参数
  6 + * @returns {Promise}
  7 + */
  8 +export function listCommunitySettingKey(params) {
  9 + return new Promise((resolve, reject) => {
  10 + request({
  11 + url: '/communitySettingKey.listCommunitySettingKey',
  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} data 保存数据
  26 + * @returns {Promise}
  27 + */
  28 +export function saveCommunitySetting(data) {
  29 + return new Promise((resolve, reject) => {
  30 + request({
  31 + url: '/community.saveCommunitySetting',
  32 + method: 'post',
  33 + data
  34 + }).then(response => {
  35 + const res = response.data
  36 + resolve(res)
  37 + }).catch(error => {
  38 + reject(error)
  39 + })
  40 + })
  41 +}
0 42 \ No newline at end of file
... ...
src/api/system/storeInfoManageApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 获取商户列表
  4 +export function listStores(params) {
  5 + return new Promise((resolve, reject) => {
  6 + request({
  7 + url: '/store.listStores',
  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 updateStoreInfo(data) {
  21 + return new Promise((resolve, reject) => {
  22 + request({
  23 + url: '/update.store.info',
  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 updateStoreAttr(data) {
  37 + return new Promise((resolve, reject) => {
  38 + request({
  39 + url: '/storeAttr.updateStoreAttr',
  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 +}
0 50 \ No newline at end of file
... ...
src/api/system/workflowManageApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +// 获取流程列表
  5 +export function listWorkflows(params) {
  6 + return new Promise((resolve, reject) => {
  7 + request({
  8 + url: '/workflow.listWorkflows',
  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 listWorkflowImage(params) {
  28 + return new Promise((resolve, reject) => {
  29 + request({
  30 + url: '/workflow.listWorkflowImage',
  31 + method: 'get',
  32 + params: {
  33 + ...params,
  34 + communityId: getCommunityId()
  35 + }
  36 + }).then(response => {
  37 + const res = response.data
  38 + if (res.code !== '0') {
  39 + reject(new Error(res.msg))
  40 + return
  41 + }
  42 + resolve({
  43 + data: res.data
  44 + })
  45 + }).catch(error => {
  46 + reject(error)
  47 + })
  48 + })
  49 +}
  50 +
  51 +// 获取流程类型字典
  52 +export function getFlowTypeDict() {
  53 + return new Promise((resolve, reject) => {
  54 + request({
  55 + url: '/dict.getDict',
  56 + method: 'get',
  57 + params: {
  58 + dictType: 'workflow',
  59 + dictCd: 'flow_type'
  60 + }
  61 + }).then(response => {
  62 + const res = response.data
  63 + resolve(res)
  64 + }).catch(error => {
  65 + reject(error)
  66 + })
  67 + })
  68 +}
0 69 \ No newline at end of file
... ...
src/api/system/workflowSettingManageApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +// 获取工作流步骤列表
  5 +export function getWorkflowSteps(params) {
  6 + return new Promise((resolve, reject) => {
  7 + request({
  8 + url: '/workflow.listWorkflowSteps',
  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 updateWorkflow(data) {
  25 + return new Promise((resolve, reject) => {
  26 + request({
  27 + url: '/workflow.updateWorkflow',
  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 queryStaffInfos(params) {
  44 + return new Promise((resolve, reject) => {
  45 + request({
  46 + url: '/query.staff.infos',
  47 + method: 'get',
  48 + params: {
  49 + ...params,
  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 listOrgTree(params) {
  63 + return new Promise((resolve, reject) => {
  64 + request({
  65 + url: '/org.listOrgTree',
  66 + method: 'get',
  67 + params: {
  68 + ...params,
  69 + 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/org/ChooseOrgTree.vue
1 1 <template>
2   - <el-dialog :title="$t('addStaff.chooseOrg')" :visible.sync="visible" width="50%" @close="handleClose">
3   - <el-tree ref="orgTree" :data="orgs" :props="defaultProps" node-key="id" highlight-current
4   - @node-click="handleNodeClick" class="org-tree-container" />
  2 + <el-dialog
  3 + :title="$t('scheduleClassesPage.selectOrg')"
  4 + :visible.sync="dialogVisible"
  5 + width="60%"
  6 + >
  7 + <el-tree
  8 + ref="orgTree"
  9 + :data="chooseOrgInfo.orgs"
  10 + :props="defaultProps"
  11 + node-key="id"
  12 + highlight-current
  13 + @node-click="handleNodeClick"
  14 + />
5 15 <span slot="footer" class="dialog-footer">
6   - <el-button @click="visible = false">{{ $t('addStaff.cancel') }}</el-button>
7   - <el-button type="primary" @click="handleConfirm">{{ $t('addStaff.confirm') }}</el-button>
  16 + <el-button @click="dialogVisible = false">{{ $t('common.cancel') }}</el-button>
  17 + <el-button type="primary" @click="_doChooseOrg">{{ $t('common.confirm') }}</el-button>
8 18 </span>
9 19 </el-dialog>
10 20 </template>
11   -
  21 +
12 22 <script>
13   -import { listOrgTree } from '@/api/staff/addStaffApi'
  23 +import { getCommunityId } from '@/api/community/communityApi'
  24 +import { listOrgTree } from '@/api/org/orgApi'
14 25  
15 26 export default {
16 27 name: 'ChooseOrgTree',
17 28 data() {
18 29 return {
19   -/************* ✨ Windsurf Command ⭐ *************/
20   -/**
21   - * Lifecycle hook called when the component is mounted.
22   - * Initializes the jsTree with the current organization data.
23   - */
24   -
25   -/******* cd76f80b-513b-4475-894b-fd5714d067a7 *******/ visible: false,
26   - orgs: [],
27   - currentOrg: null,
  30 + dialogVisible: false,
  31 + chooseOrgInfo: {
  32 + orgs: [],
  33 + orgId: '',
  34 + curOrg: {}
  35 + },
28 36 defaultProps: {
29 37 children: 'children',
30   - label: 'text'
  38 + label: 'name'
31 39 }
32 40 }
33 41 },
34 42 methods: {
35   - openOrgModal() {
36   - this.visible = true
37   - this.loadOrgs()
  43 + open() {
  44 + this.dialogVisible = true
  45 + this._loadChooseOrgs()
38 46 },
39   - async loadOrgs() {
40   - this.orgs = []
  47 + async _loadChooseOrgs() {
41 48 try {
42   - const res = await listOrgTree({
43   - communityId: '-1'
44   - })
45   - this.orgs.push(res.data)
  49 + const params = {
  50 + communityId: getCommunityId()
  51 + }
  52 + const { data } = await listOrgTree(params)
  53 + this.chooseOrgInfo.orgs = data
46 54 } catch (error) {
47   - this.$message.error(error.message || '加载组织树失败')
  55 + console.error('获取组织树失败:', error)
48 56 }
49 57 },
50 58 handleNodeClick(data) {
51   - this.currentOrg = {
52   - orgId: data.id,
53   - allOrgName: data.text
54   - }
  59 + this.chooseOrgInfo.curOrg = data
  60 + this.chooseOrgInfo.curOrg.orgId = data.id
55 61 },
56   - handleConfirm() {
57   - if (!this.currentOrg) {
58   - this.$message.warning('请选择组织')
59   - return
60   - }
61   - this.$emit('switchOrg', this.currentOrg)
62   - this.visible = false
63   - },
64   - handleClose() {
65   - this.currentOrg = null
66   - this.$refs.orgTree.setCurrentKey(null)
  62 + _doChooseOrg() {
  63 + this.$emit('switchOrg', this.chooseOrgInfo.curOrg)
  64 + this.dialogVisible = false
67 65 }
68 66 }
69 67 }
70 68 </script>
71   -
  69 +
72 70 <style lang="scss" scoped>
73   -.org-tree-container {
74   - min-height: 300px;
75   - max-height: 500px;
  71 +::v-deep .el-dialog__body {
  72 + padding: 20px;
  73 + max-height: 60vh;
76 74 overflow-y: auto;
77 75 }
78 76 </style>
79 77 \ No newline at end of file
... ...
src/components/org/deleteScheduleClasses.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('scheduleClasses.delete.title')"
  4 + :visible.sync="visible"
  5 + width="30%"
  6 + @close="handleClose"
  7 + >
  8 + <div style="text-align: center">
  9 + <p>{{ $t('scheduleClasses.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 { deleteScheduleClasses } from '@/api/org/scheduleClassesApi'
  20 +
  21 +export default {
  22 + name: 'DeleteScheduleClasses',
  23 + data() {
  24 + return {
  25 + visible: false,
  26 + currentItem: null
  27 + }
  28 + },
  29 + methods: {
  30 + open(item) {
  31 + this.currentItem = item
  32 + this.visible = true
  33 + },
  34 + async handleConfirm() {
  35 + try {
  36 + await deleteScheduleClasses(this.currentItem)
  37 + this.$message.success(this.$t('scheduleClasses.delete.success'))
  38 + this.$emit('success')
  39 + this.visible = false
  40 + } catch (error) {
  41 + this.$message.error(this.$t('scheduleClasses.delete.error'))
  42 + }
  43 + },
  44 + handleClose() {
  45 + this.visible = false
  46 + }
  47 + }
  48 +}
  49 +</script>
0 50 \ No newline at end of file
... ...
src/components/org/editScheduleClassesDay.vue 0 → 100644
  1 +<template>
  2 + <el-dialog :title="$t('editScheduleClassesDay.title')" :visible.sync="visible" width="60%" @close="handleClose">
  3 + <el-form label-position="right" label-width="120px">
  4 + <el-form-item :label="$t('editScheduleClassesDay.status')">
  5 + <el-select v-model="workday" @change="changeScheduleClassesDayState" style="width:100%">
  6 + <el-option :label="$t('editScheduleClassesDay.rest')" value="2002" />
  7 + <el-option v-for="(item, index) in classess" :key="index" :label="`${item.name}${getClassTime(item)}`"
  8 + :value="item.classesId" />
  9 + </el-select>
  10 + </el-form-item>
  11 +
  12 + <div v-if="workday !== '2002'">
  13 + <el-form-item v-for="(item, index) in times" :key="index">
  14 + <el-col :span="11">
  15 + <el-form-item :label="$t('editScheduleClassesDay.startTime')">
  16 + <el-input v-model="item.startTime" :placeholder="$t('editScheduleClassesDay.startTimePlaceholder')" />
  17 + </el-form-item>
  18 + </el-col>
  19 + <el-col :span="11" :offset="2">
  20 + <el-form-item :label="$t('editScheduleClassesDay.endTime')">
  21 + <el-input v-model="item.endTime" :placeholder="$t('editScheduleClassesDay.endTimePlaceholder')" />
  22 + </el-form-item>
  23 + </el-col>
  24 + </el-form-item>
  25 + </div>
  26 + </el-form>
  27 +
  28 + <span slot="footer" class="dialog-footer">
  29 + <el-button @click="visible = false">{{ $t('common.cancel') }}</el-button>
  30 + <el-button type="primary" @click="submitEditScheduleClassesDay">
  31 + {{ $t('common.submit') }}
  32 + </el-button>
  33 + </span>
  34 + </el-dialog>
  35 +</template>
  36 +
  37 +<script>
  38 +import { listClasses } from '@/api/org/addScheduleClassesApi'
  39 +
  40 +export default {
  41 + name: 'EditScheduleClassesDay',
  42 + data() {
  43 + return {
  44 + visible: false,
  45 + workday: '',
  46 + workdayName: '',
  47 + times: [],
  48 + classess: [],
  49 + currentItem: null
  50 + }
  51 + },
  52 + methods: {
  53 + open(item) {
  54 + this.currentItem = item
  55 + this.workday = item.workday
  56 + this.workdayName = item.workdayName
  57 + this.times = item.times ? [...item.times] : []
  58 + this.listClassess()
  59 + this.visible = true
  60 + },
  61 + handleClose() {
  62 + this.workday = ''
  63 + this.workdayName = ''
  64 + this.times = []
  65 + this.currentItem = null
  66 + },
  67 + async listClassess() {
  68 + try {
  69 + const params = {
  70 + page: 1,
  71 + row: 100,
  72 + state: '1001'
  73 + }
  74 + const res = await listClasses(params)
  75 + this.classess = res.data || []
  76 + } catch (error) {
  77 + console.error('获取班次列表失败:', error)
  78 + }
  79 + },
  80 + changeScheduleClassesDayState() {
  81 + this.times = []
  82 + if (this.workday === '2002') {
  83 + this.workdayName = this.$t('editScheduleClassesDay.rest')
  84 + return
  85 + }
  86 +
  87 + const selectedClass = this.classess.find(item => item.classesId === this.workday)
  88 + if (selectedClass) {
  89 + this.workdayName = selectedClass.name
  90 + if (selectedClass.times) {
  91 + this.times = [...selectedClass.times]
  92 + }
  93 + }
  94 + },
  95 + getClassTime(item) {
  96 + if (!item.times || item.times.length === 0) return ''
  97 +
  98 + let timeStr = ''
  99 + item.times.forEach(timeItem => {
  100 + timeStr += `${timeItem.startTime}-${timeItem.endTime};`
  101 + })
  102 +
  103 + return `(${timeStr})`
  104 + },
  105 + submitEditScheduleClassesDay() {
  106 + if (this.currentItem) {
  107 + this.currentItem.workday = this.workday
  108 + this.currentItem.workdayName = this.workdayName
  109 + this.currentItem.times = [...this.times]
  110 + this.visible = false
  111 + }
  112 + }
  113 + }
  114 +}
  115 +</script>
  116 +
  117 +<style lang="scss" scoped>
  118 +.el-form-item {
  119 + margin-bottom: 20px;
  120 +}
  121 +</style>
0 122 \ No newline at end of file
... ...
src/components/org/scheduleClassesDay.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <el-form-item :label="$t('scheduleClassesDay.scheduleCycle')">
  4 + <el-select v-model="scheduleCycle" :placeholder="$t('scheduleClassesDay.scheduleCyclePlaceholder')"
  5 + @change="changeInspectionPeriod" style="width:100%">
  6 + <el-option v-for="index in 31" :key="index" :label="`${index}${$t('scheduleClassesDay.day')}`" :value="index" />
  7 + </el-select>
  8 + </el-form-item>
  9 +
  10 + <el-form-item :label="$t('scheduleClassesDay.scheduleInfo')">
  11 + <div class="schedule-days-container">
  12 + <div v-for="(item, index) in days" :key="index" class="schedule-day-item" @click="changeWorkdayInfo(item)">
  13 + <div class="day-number">{{ item.day }}</div>
  14 + <div class="workday-name">{{ item.workdayName }}</div>
  15 + <div v-for="(time, timeIndex) in item.times" :key="timeIndex" class="time-range">
  16 + {{ time.startTime }}-{{ time.endTime }}
  17 + </div>
  18 + </div>
  19 + </div>
  20 + </el-form-item>
  21 + </div>
  22 +</template>
  23 +
  24 +<script>
  25 +export default {
  26 + name: 'ScheduleClassesDay',
  27 + props: {
  28 + initialData: {
  29 + type: Object,
  30 + default: () => ({})
  31 + }
  32 + },
  33 + data() {
  34 + return {
  35 + scheduleCycle: 1,
  36 + days: []
  37 + }
  38 + },
  39 + created() {
  40 + if (this.initialData.days && this.initialData.days.length > 0) {
  41 + this.days = [...this.initialData.days]
  42 + this.scheduleCycle = this.initialData.scheduleCycle
  43 + } else {
  44 + this.changeInspectionPeriod()
  45 + }
  46 + },
  47 + methods: {
  48 + notify(params) {
  49 + this.localDays = params.days || []
  50 + this.$emit('update:scheduleCycle', params.scheduleCycle || 1)
  51 + if (!params.days || params.days.length === 0) {
  52 + this.changeInspectionPeriodWeek()
  53 + }
  54 + },
  55 + initData(scheduleCycle) {
  56 + this.scheduleCycle = scheduleCycle
  57 + this.changeInspectionPeriod()
  58 + },
  59 + changeInspectionPeriod() {
  60 + this.days = []
  61 + for (let cycleIndex = 0; cycleIndex < this.scheduleCycle; cycleIndex++) {
  62 + this.days.push({
  63 + day: cycleIndex + 1,
  64 + workday: '2002',
  65 + workdayName: this.$t('scheduleClassesDay.rest'),
  66 + times: []
  67 + })
  68 + }
  69 + this.$emit('cycle-change', this.scheduleCycle)
  70 + },
  71 + changeWorkdayInfo(item) {
  72 + this.$emit('editDay', item)
  73 + },
  74 + getDaysData() {
  75 + return this.days
  76 + }
  77 + }
  78 +}
  79 +</script>
  80 +
  81 +<style lang="scss" scoped>
  82 +.schedule-days-container {
  83 + display: flex;
  84 + flex-wrap: wrap;
  85 + gap: 10px;
  86 +}
  87 +
  88 +.schedule-day-item {
  89 + width: calc(16.66% - 10px);
  90 + padding: 10px;
  91 + border: 1px solid #ebeef5;
  92 + border-radius: 4px;
  93 + cursor: pointer;
  94 + text-align: center;
  95 + transition: all 0.3s;
  96 +
  97 + &:hover {
  98 + border-color: #409eff;
  99 + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  100 + }
  101 +
  102 + .day-number {
  103 + font-weight: bold;
  104 + margin-bottom: 5px;
  105 + }
  106 +
  107 + .workday-name {
  108 + margin-bottom: 5px;
  109 + }
  110 +
  111 + .time-range {
  112 + font-size: 12px;
  113 + color: #666;
  114 + }
  115 +}
  116 +</style>
0 117 \ No newline at end of file
... ...
src/components/org/scheduleClassesMonth.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <el-form-item :label="$t('scheduleClassesMonth.scheduleCycle')">
  4 + <el-select
  5 + v-model="scheduleCycle"
  6 + :placeholder="$t('scheduleClassesMonth.scheduleCyclePlaceholder')"
  7 + @change="changeInspectionPeriod"
  8 + style="width:100%"
  9 + >
  10 + <el-option
  11 + :label="`1${$t('scheduleClassesMonth.month')}`"
  12 + value="1"
  13 + />
  14 + </el-select>
  15 + </el-form-item>
  16 +
  17 + <el-form-item :label="$t('scheduleClassesMonth.scheduleInfo')">
  18 + <div class="schedule-days-container">
  19 + <div
  20 + v-for="(item, index) in days"
  21 + :key="index"
  22 + class="schedule-day-item"
  23 + @click="changeWorkdayInfo(item)"
  24 + >
  25 + <div class="day-number">{{ item.day }}</div>
  26 + <div class="workday-name">{{ item.workdayName }}</div>
  27 + <div
  28 + v-for="(time, timeIndex) in item.times"
  29 + :key="timeIndex"
  30 + class="time-range"
  31 + >
  32 + {{ time.startTime }}-{{ time.endTime }}
  33 + </div>
  34 + </div>
  35 + </div>
  36 + </el-form-item>
  37 + </div>
  38 +</template>
  39 +
  40 +<script>
  41 +export default {
  42 + name: 'ScheduleClassesMonth',
  43 + props: {
  44 + initialData: {
  45 + type: Object,
  46 + default: () => ({})
  47 + }
  48 + },
  49 + data() {
  50 + return {
  51 + scheduleCycle: 1,
  52 + days: []
  53 + }
  54 + },
  55 + created() {
  56 + if (this.initialData.days && this.initialData.days.length > 0) {
  57 + this.days = [...this.initialData.days]
  58 + this.scheduleCycle = this.initialData.scheduleCycle
  59 + } else {
  60 + this.changeInspectionPeriod()
  61 + }
  62 + },
  63 + methods: {
  64 + initData(scheduleCycle) {
  65 + this.scheduleCycle = scheduleCycle
  66 + this.changeInspectionPeriod()
  67 + },
  68 + changeInspectionPeriod() {
  69 + this.days = []
  70 + for (let cycleIndex = 0; cycleIndex < 31; cycleIndex++) {
  71 + this.days.push({
  72 + day: cycleIndex + 1,
  73 + workday: '2002',
  74 + workdayName: this.$t('scheduleClassesMonth.rest'),
  75 + times: []
  76 + })
  77 + }
  78 + },
  79 + changeWorkdayInfo(item) {
  80 + this.$emit('edit-day', item)
  81 + },
  82 + getDaysData() {
  83 + return this.days
  84 + }
  85 + }
  86 +}
  87 +</script>
  88 +
  89 +<style lang="scss" scoped>
  90 +.schedule-days-container {
  91 + display: flex;
  92 + flex-wrap: wrap;
  93 + gap: 10px;
  94 +}
  95 +
  96 +.schedule-day-item {
  97 + width: calc(16.66% - 10px);
  98 + padding: 10px;
  99 + border: 1px solid #ebeef5;
  100 + border-radius: 4px;
  101 + cursor: pointer;
  102 + text-align: center;
  103 + transition: all 0.3s;
  104 +
  105 + &:hover {
  106 + border-color: #409eff;
  107 + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  108 + }
  109 +
  110 + .day-number {
  111 + font-weight: bold;
  112 + margin-bottom: 5px;
  113 + }
  114 +
  115 + .workday-name {
  116 + margin-bottom: 5px;
  117 + }
  118 +
  119 + .time-range {
  120 + font-size: 12px;
  121 + color: #666;
  122 + }
  123 +}
  124 +</style>
0 125 \ No newline at end of file
... ...
src/components/org/scheduleClassesState.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('scheduleClasses.state.title')"
  4 + :visible.sync="visible"
  5 + width="30%"
  6 + @close="handleClose"
  7 + >
  8 + <div style="text-align: center">
  9 + <p>
  10 + {{ $t('scheduleClasses.state.confirm') }}
  11 + <strong>{{ formData.stateName }}</strong>
  12 + {{ $t('scheduleClasses.state.schedule') }}
  13 + </p>
  14 + </div>
  15 + <span slot="footer" class="dialog-footer">
  16 + <el-button @click="handleClose">{{ $t('common.cancel') }}</el-button>
  17 + <el-button type="primary" @click="handleConfirm">{{ $t('common.confirm') }}</el-button>
  18 + </span>
  19 + </el-dialog>
  20 +</template>
  21 +
  22 +<script>
  23 +import { updateScheduleClassesState } from '@/api/org/scheduleClassesApi'
  24 +
  25 +export default {
  26 + name: 'ScheduleClassesState',
  27 + data() {
  28 + return {
  29 + visible: false,
  30 + formData: {
  31 + scheduleId: '',
  32 + stateName: '',
  33 + state: ''
  34 + }
  35 + }
  36 + },
  37 + methods: {
  38 + open(data) {
  39 + this.formData = { ...data }
  40 + this.visible = true
  41 + },
  42 + async handleConfirm() {
  43 + try {
  44 + await updateScheduleClassesState(this.formData)
  45 + this.$message.success(this.$t('scheduleClasses.state.success'))
  46 + this.$emit('success')
  47 + this.visible = false
  48 + } catch (error) {
  49 + this.$message.error(this.$t('scheduleClasses.state.error'))
  50 + }
  51 + },
  52 + handleClose() {
  53 + this.visible = false
  54 + }
  55 + }
  56 +}
  57 +</script>
0 58 \ No newline at end of file
... ...
src/components/org/scheduleClassesWeek.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <el-form-item :label="$t('scheduleClassesWeek.scheduleCycle')">
  4 + <el-select v-model="scheduleCycle" :placeholder="$t('scheduleClassesWeek.scheduleCyclePlaceholder')"
  5 + @change="changeInspectionPeriodWeek" style="width:100%">
  6 + <el-option v-for="index in 4" :key="index" :label="`${index}${$t('scheduleClassesWeek.week')}`"
  7 + :value="index" />
  8 + </el-select>
  9 + </el-form-item>
  10 +
  11 + <div v-for="week in scheduleCycle" :key="week">
  12 + <el-form-item :label="getWeek(week)">
  13 + <div class="schedule-weeks-container">
  14 + <template v-for="(item, index) in days">
  15 + <div v-if="item.weekFlag === week" :key="index" class="schedule-week-item" @click="changeWorkdayWeekInfo(item)">
  16 + <div class="day-name">{{ getWorkDay(item.day) }}</div>
  17 + <div class="workday-name">{{ item.workdayName }}</div>
  18 + <div v-for="(time, timeIndex) in item.times" :key="timeIndex" class="time-range">
  19 + {{ time.startTime }}-{{ time.endTime }}
  20 + </div>
  21 + </div>
  22 + </template>
  23 + </div>
  24 + </el-form-item>
  25 + </div>
  26 + </div>
  27 +</template>
  28 +
  29 +<script>
  30 +export default {
  31 + name: 'ScheduleClassesWeek',
  32 + props: {
  33 + initialData: {
  34 + type: Object,
  35 + default: () => ({})
  36 + }
  37 + },
  38 + data() {
  39 + return {
  40 + scheduleCycle: 1,
  41 + days: []
  42 + }
  43 + },
  44 + created() {
  45 + if (this.initialData.days && this.initialData.days.length > 0) {
  46 + this.days = [...this.initialData.days]
  47 + this.scheduleCycle = this.initialData.scheduleCycle
  48 + } else {
  49 + this.changeInspectionPeriodWeek()
  50 + }
  51 + },
  52 + methods: {
  53 + initData(scheduleCycle) {
  54 + this.scheduleCycle = scheduleCycle
  55 + this.changeInspectionPeriodWeek()
  56 + },
  57 + changeInspectionPeriodWeek() {
  58 + this.days = []
  59 + for (let weekIndex = 0; weekIndex < this.scheduleCycle; weekIndex++) {
  60 + for (let cycleIndex = 0; cycleIndex < 7; cycleIndex++) {
  61 + this.days.push({
  62 + weekFlag: weekIndex + 1,
  63 + day: cycleIndex + 1,
  64 + workday: '2002',
  65 + workdayName: this.$t('scheduleClassesWeek.rest'),
  66 + times: []
  67 + })
  68 + }
  69 + }
  70 + this.$emit('cycle-change', this.scheduleCycle)
  71 + },
  72 + changeWorkdayWeekInfo(item) {
  73 + this.$emit('edit-day', item)
  74 + },
  75 + getWeek(week) {
  76 + return `${week}${this.$t('scheduleClassesWeek.week')}`
  77 + },
  78 + getWorkDay(day) {
  79 + const days = [
  80 + this.$t('common.monday'),
  81 + this.$t('common.tuesday'),
  82 + this.$t('common.wednesday'),
  83 + this.$t('common.thursday'),
  84 + this.$t('common.friday'),
  85 + this.$t('common.saturday'),
  86 + this.$t('common.sunday')
  87 + ]
  88 + return days[day - 1]
  89 + },
  90 + getDaysData() {
  91 + return this.days
  92 + }
  93 + }
  94 +}
  95 +</script>
  96 +
  97 +<style lang="scss" scoped>
  98 +.schedule-weeks-container {
  99 + display: flex;
  100 + flex-wrap: wrap;
  101 + gap: 10px;
  102 +}
  103 +
  104 +.schedule-week-item {
  105 + width: calc(14.28% - 10px);
  106 + padding: 10px;
  107 + border: 1px solid #ebeef5;
  108 + border-radius: 4px;
  109 + cursor: pointer;
  110 + text-align: center;
  111 + transition: all 0.3s;
  112 +
  113 + &:hover {
  114 + border-color: #409eff;
  115 + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  116 + }
  117 +
  118 + .day-name {
  119 + font-weight: bold;
  120 + margin-bottom: 5px;
  121 + }
  122 +
  123 + .workday-name {
  124 + margin-bottom: 5px;
  125 + }
  126 +
  127 + .time-range {
  128 + font-size: 12px;
  129 + color: #666;
  130 + }
  131 +}
  132 +</style>
0 133 \ No newline at end of file
... ...
src/components/system/editStoreAttr.vue 0 → 100644
  1 +<template>
  2 + <el-dialog :title="$t('editStoreAttr.title')" :visible.sync="visible" width="60%" @close="handleClose">
  3 + <el-form ref="form" :model="form" :rules="rules" label-width="120px" label-position="right">
  4 + <el-form-item :label="form.name">
  5 + <el-input v-model="form.value" :placeholder="$t('editStoreAttr.attributePlaceholder')" />
  6 + </el-form-item>
  7 + </el-form>
  8 + <span slot="footer" class="dialog-footer">
  9 + <el-button @click="visible = false">
  10 + {{ $t('common.cancel') }}
  11 + </el-button>
  12 + <el-button type="primary" @click="submitForm">
  13 + {{ $t('common.save') }}
  14 + </el-button>
  15 + </span>
  16 + </el-dialog>
  17 +</template>
  18 +
  19 +<script>
  20 +import { updateStoreAttr } from '@/api/system/storeInfoManageApi'
  21 +
  22 +export default {
  23 + name: 'EditStoreAttr',
  24 + data() {
  25 + return {
  26 + visible: false,
  27 + form: {
  28 + attrId: '',
  29 + value: '',
  30 + name: ''
  31 + },
  32 + rules: {
  33 + value: [
  34 + { required: true, message: this.$t('editStoreAttr.validate.valueRequired'), trigger: 'blur' },
  35 + { max: 50, message: this.$t('editStoreAttr.validate.valueMaxLength'), trigger: 'blur' }
  36 + ],
  37 + attrId: [
  38 + { required: true, message: this.$t('editStoreAttr.validate.attrIdRequired'), trigger: 'blur' }
  39 + ]
  40 + }
  41 + }
  42 + },
  43 + methods: {
  44 + open(storeAttr) {
  45 + this.resetForm()
  46 + this.form = {
  47 + ...this.form,
  48 + ...storeAttr
  49 + }
  50 + this.visible = true
  51 + },
  52 + resetForm() {
  53 + this.form = {
  54 + attrId: '',
  55 + value: '',
  56 + name: ''
  57 + }
  58 + },
  59 + handleClose() {
  60 + this.resetForm()
  61 + },
  62 + submitForm() {
  63 + this.$refs.form.validate(async valid => {
  64 + if (valid) {
  65 + try {
  66 + await updateStoreAttr(this.form)
  67 + this.$message.success(this.$t('common.saveSuccess'))
  68 + this.visible = false
  69 + this.$emit('success')
  70 + } catch (error) {
  71 + this.$message.error(error.message || this.$t('common.saveFailed'))
  72 + }
  73 + }
  74 + })
  75 + }
  76 + }
  77 +}
  78 +</script>
0 79 \ No newline at end of file
... ...
src/components/system/editStoreInfo.vue 0 → 100644
  1 +<template>
  2 + <el-dialog :title="$t('editStoreInfo.title')" :visible.sync="visible" width="60%" @close="handleClose">
  3 + <el-form ref="form" :model="form" :rules="rules" label-width="120px" label-position="right">
  4 + <el-form-item :label="$t('editStoreInfo.storeName')" prop="name">
  5 + <el-input v-model="form.name" :placeholder="$t('editStoreInfo.storeNamePlaceholder')" disabled />
  6 + </el-form-item>
  7 + <el-form-item :label="$t('editStoreInfo.storeAddress')" prop="address">
  8 + <el-input v-model="form.address" :placeholder="$t('editStoreInfo.storeAddressPlaceholder')" />
  9 + </el-form-item>
  10 + <el-form-item :label="$t('editStoreInfo.contactNumber')" prop="tel">
  11 + <el-input v-model="form.tel" :placeholder="$t('editStoreInfo.contactNumberPlaceholder')" />
  12 + </el-form-item>
  13 + <el-form-item :label="$t('editStoreInfo.nearbyLandmark')" prop="nearByLandmarks">
  14 + <el-input v-model="form.nearByLandmarks" :placeholder="$t('editStoreInfo.nearbyLandmarkPlaceholder')" />
  15 + </el-form-item>
  16 + <el-form-item :label="$t('editStoreInfo.mapX')" prop="mapX">
  17 + <el-input v-model="form.mapX" :placeholder="$t('editStoreInfo.mapXPlaceholder')" />
  18 + </el-form-item>
  19 + <el-form-item :label="$t('editStoreInfo.mapY')" prop="mapY">
  20 + <el-input v-model="form.mapY" :placeholder="$t('editStoreInfo.mapYPlaceholder')" />
  21 + </el-form-item>
  22 + </el-form>
  23 + <span slot="footer" class="dialog-footer">
  24 + <el-button @click="visible = false">
  25 + {{ $t('common.cancel') }}
  26 + </el-button>
  27 + <el-button type="primary" @click="submitForm">
  28 + {{ $t('common.save') }}
  29 + </el-button>
  30 + </span>
  31 + </el-dialog>
  32 +</template>
  33 +
  34 +<script>
  35 +import { updateStoreInfo } from '@/api/system/storeInfoManageApi'
  36 +
  37 +export default {
  38 + name: 'EditStoreInfo',
  39 + data() {
  40 + return {
  41 + visible: false,
  42 + form: {
  43 + storeId: '',
  44 + name: '',
  45 + address: '',
  46 + tel: '',
  47 + nearByLandmarks: '',
  48 + mapX: '',
  49 + mapY: ''
  50 + },
  51 + rules: {
  52 + name: [
  53 + { required: true, message: this.$t('editStoreInfo.validate.nameRequired'), trigger: 'blur' },
  54 + { max: 100, message: this.$t('editStoreInfo.validate.nameMaxLength'), trigger: 'blur' }
  55 + ],
  56 + address: [
  57 + { required: true, message: this.$t('editStoreInfo.validate.addressRequired'), trigger: 'blur' },
  58 + { max: 200, message: this.$t('editStoreInfo.validate.addressMaxLength'), trigger: 'blur' }
  59 + ],
  60 + tel: [
  61 + { required: true, message: this.$t('editStoreInfo.validate.telRequired'), trigger: 'blur' },
  62 + { max: 11, message: this.$t('editStoreInfo.validate.telMaxLength'), trigger: 'blur' }
  63 + ],
  64 + nearByLandmarks: [
  65 + { required: true, message: this.$t('editStoreInfo.validate.landmarkRequired'), trigger: 'blur' },
  66 + { max: 200, message: this.$t('editStoreInfo.validate.landmarkMaxLength'), trigger: 'blur' }
  67 + ]
  68 + }
  69 + }
  70 + },
  71 + methods: {
  72 + open(storeInfo) {
  73 + this.resetForm()
  74 + this.form = {
  75 + ...this.form,
  76 + ...storeInfo
  77 + }
  78 + this.visible = true
  79 + },
  80 + resetForm() {
  81 + this.form = {
  82 + storeId: '',
  83 + name: '',
  84 + address: '',
  85 + tel: '',
  86 + nearByLandmarks: '',
  87 + mapX: '',
  88 + mapY: ''
  89 + }
  90 + },
  91 + handleClose() {
  92 + this.resetForm()
  93 + },
  94 + submitForm() {
  95 + this.$refs.form.validate(async valid => {
  96 + if (valid) {
  97 + try {
  98 + await updateStoreInfo(this.form)
  99 + this.$message.success(this.$t('common.saveSuccess'))
  100 + this.visible = false
  101 + this.$emit('success')
  102 + } catch (error) {
  103 + this.$message.error(error.message || this.$t('common.saveFailed'))
  104 + }
  105 + }
  106 + })
  107 + }
  108 + }
  109 +}
  110 +</script>
0 111 \ No newline at end of file
... ...
src/components/system/orgTreeShow.vue 0 → 100644
  1 +<template>
  2 + <div class="org-tree-show">
  3 + <el-tree
  4 + ref="orgTree"
  5 + :data="orgData"
  6 + :props="defaultProps"
  7 + node-key="id"
  8 + default-expand-all
  9 + highlight-current
  10 + @node-click="handleNodeClick"
  11 + />
  12 + </div>
  13 +</template>
  14 +
  15 +<script>
  16 +import { listOrgTree } from '@/api/system/workflowSettingManageApi'
  17 +import { getCommunityId } from '@/api/community/communityApi'
  18 +
  19 +export default {
  20 + name: 'OrgTreeShow',
  21 + data() {
  22 + return {
  23 + communityId: '',
  24 + orgData: [],
  25 + defaultProps: {
  26 + children: 'children',
  27 + label: 'text'
  28 + }
  29 + }
  30 + },
  31 + methods: {
  32 + async initTree() {
  33 + this.communityId = getCommunityId()
  34 + try {
  35 + const { data } = await listOrgTree({
  36 + communityId: this.communityId
  37 + })
  38 + this.orgData = data || []
  39 + } catch (error) {
  40 + this.$message.error(this.$t('orgTree.fetchError'))
  41 + }
  42 + },
  43 +
  44 + handleNodeClick(data) {
  45 + this.$emit('switch-org', {
  46 + orgId: data.id,
  47 + orgName: data.text
  48 + })
  49 + }
  50 + }
  51 +}
  52 +</script>
  53 +
  54 +<style lang="scss" scoped>
  55 +.org-tree-show {
  56 + /deep/ .el-tree {
  57 + .el-tree-node {
  58 + .el-tree-node__content {
  59 + height: 40px;
  60 +
  61 + &:hover {
  62 + background-color: #f5f7fa;
  63 + }
  64 + }
  65 +
  66 + &.is-current {
  67 + > .el-tree-node__content {
  68 + background-color: #ecf5ff;
  69 + color: #409eff;
  70 + }
  71 + }
  72 + }
  73 + }
  74 +}
  75 +</style>
0 76 \ No newline at end of file
... ...
src/components/system/selectStaff.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :visible.sync="visible"
  4 + :title="$t('selectStaff.title')"
  5 + width="70%"
  6 + @close="handleClose"
  7 + >
  8 + <el-row :gutter="20">
  9 + <el-col :span="12" class="border-right">
  10 + <div class="text-center mb-20">
  11 + <h4>{{ $t('selectStaff.orgInfo') }}</h4>
  12 + </div>
  13 + <div class="org-tree-container">
  14 + <org-tree-show
  15 + ref="orgTree"
  16 + @switch-org="handleSwitchOrg"
  17 + />
  18 + </div>
  19 + </el-col>
  20 +
  21 + <el-col :span="12">
  22 + <div class="text-center mb-20">
  23 + <h4>{{ $t('selectStaff.staffInfo') }}</h4>
  24 + </div>
  25 + <div class="staff-list">
  26 + <div
  27 + v-for="(item,index) in staffList"
  28 + :key="index"
  29 + class="staff-item"
  30 + :class="{ 'active': currentStaffId === item.userId }"
  31 + @click="handleSelectStaff(item)"
  32 + >
  33 + <div>
  34 + <i class="el-icon-user margin-right-xs"></i>
  35 + {{ item.userName }}
  36 + </div>
  37 + <div>{{ item.tel }}</div>
  38 + </div>
  39 + </div>
  40 + </el-col>
  41 + </el-row>
  42 +
  43 + <div
  44 + v-if="staff.from === 'bpmn' || staff.from === 'purchase' || staff.from === 'contract'"
  45 + slot="footer"
  46 + class="dialog-footer"
  47 + >
  48 + <el-button @click="handleFirstUser">{{ $t('selectStaff.submitter') }}</el-button>
  49 + <el-button @click="handleCustomUser">{{ $t('selectStaff.dynamicAssign') }}</el-button>
  50 + </div>
  51 + </el-dialog>
  52 +</template>
  53 +
  54 +<script>
  55 +import { queryStaffInfos } from '@/api/system/workflowSettingManageApi'
  56 +import OrgTreeShow from './OrgTreeShow'
  57 +
  58 +export default {
  59 + name: 'SelectStaff',
  60 + components: {
  61 + OrgTreeShow
  62 + },
  63 + data() {
  64 + return {
  65 + visible: false,
  66 + staff: {},
  67 + staffList: [],
  68 + currentStaffId: '',
  69 + currentOrg: null
  70 + }
  71 + },
  72 + methods: {
  73 + open(staff) {
  74 + this.staff = staff
  75 + this.visible = true
  76 + this.$nextTick(() => {
  77 + this.$refs.orgTree.initTree()
  78 + })
  79 + },
  80 +
  81 + handleClose() {
  82 + this.staffList = []
  83 + this.currentStaffId = ''
  84 + this.currentOrg = null
  85 + },
  86 +
  87 + async handleSwitchOrg(org) {
  88 + this.currentOrg = org
  89 + try {
  90 + const { data } = await queryStaffInfos({
  91 + page: 1,
  92 + row: 50,
  93 + orgId: org.orgId
  94 + })
  95 + this.staffList = data.staffs || []
  96 + if (this.staffList.length > 0) {
  97 + this.currentStaffId = this.staffList[0].userId
  98 + }
  99 + } catch (error) {
  100 + this.$message.error(this.$t('selectStaff.fetchStaffError'))
  101 + }
  102 + },
  103 +
  104 + handleSelectStaff(item) {
  105 + this.staff.staffId = item.userId
  106 + this.staff.staffName = item.userName
  107 + this.visible = false
  108 + if (this.staff.call && typeof this.staff.call === 'function') {
  109 + this.staff.call(this.staff)
  110 + }
  111 + },
  112 +
  113 + handleFirstUser() {
  114 + this.staff.staffId = '${startUserId}'
  115 + this.staff.staffName = this.$t('selectStaff.submitter')
  116 + this.visible = false
  117 + if (this.staff.call && typeof this.staff.call === 'function') {
  118 + this.staff.call(this.staff)
  119 + }
  120 + },
  121 +
  122 + handleCustomUser() {
  123 + this.staff.staffId = '${nextUserId}'
  124 + this.staff.staffName = this.$t('selectStaff.dynamicAssign')
  125 + this.visible = false
  126 + if (this.staff.call && typeof this.staff.call === 'function') {
  127 + this.staff.call(this.staff)
  128 + }
  129 + }
  130 + }
  131 +}
  132 +</script>
  133 +
  134 +<style lang="scss" scoped>
  135 +.border-right {
  136 + border-right: 1px solid #ebeef5;
  137 +}
  138 +
  139 +.org-tree-container {
  140 + height: 400px;
  141 + overflow-y: auto;
  142 +}
  143 +
  144 +.staff-list {
  145 + height: 400px;
  146 + overflow-y: auto;
  147 +
  148 + .staff-item {
  149 + padding: 10px;
  150 + margin-bottom: 10px;
  151 + cursor: pointer;
  152 + border-radius: 4px;
  153 +
  154 + &:hover {
  155 + background-color: #f5f7fa;
  156 + }
  157 +
  158 + &.active {
  159 + background-color: #ecf5ff;
  160 + color: #409eff;
  161 + }
  162 + }
  163 +}
  164 +
  165 +.margin-right-xs {
  166 + margin-right: 5px;
  167 +}
  168 +
  169 +.mb-20 {
  170 + margin-bottom: 20px;
  171 +}
  172 +
  173 +.dialog-footer {
  174 + text-align: right;
  175 +}
  176 +</style>
0 177 \ No newline at end of file
... ...
src/components/system/viewImage.vue
1 1 <template>
2   - <div class="image-viewer">
3   - <el-dialog
4   - :visible.sync="dialogVisible"
5   - fullscreen
  2 + <div class="view-image-container">
  3 + <el-dialog
  4 + :visible.sync="visible"
  5 + :fullscreen="true"
6 6 :show-close="false"
7   - @close="closeDialog">
8   - <div class="image-container">
9   - <el-image
10   - :src="imageUrl"
11   - fit="contain"
12   - :style="{ width: imageWidth + 'px', height: imageHeight + 'px' }">
13   - </el-image>
14   - <i class="el-icon-close close-icon" @click="closeDialog"></i>
  7 + custom-class="image-viewer-dialog"
  8 + >
  9 + <div class="image-wrapper">
  10 + <img
  11 + :src="imageInfo.url"
  12 + :style="{
  13 + width: imageInfo.width + 'px',
  14 + height: imageInfo.height + 'px'
  15 + }"
  16 + @error="handleImageError"
  17 + />
  18 + <i class="el-icon-close close-icon" @click="close"></i>
15 19 </div>
16 20 </el-dialog>
17 21 </div>
... ... @@ -22,50 +26,74 @@ export default {
22 26 name: 'ViewImage',
23 27 data() {
24 28 return {
25   - dialogVisible: false,
26   - imageUrl: '',
27   - imageWidth: 800,
28   - imageHeight: 800
  29 + visible: false,
  30 + imageInfo: {
  31 + url: '',
  32 + width: 800,
  33 + height: 800
  34 + }
29 35 }
30 36 },
31 37 methods: {
32   - open(url) {
33   - this.imageUrl = url
34   - this.dialogVisible = true
  38 + open(params) {
  39 + this.imageInfo.url = params.url
  40 + this.visible = true
35 41  
36   - // 计算图片宽高比例
  42 + // 动态计算图片尺寸
37 43 const img = new Image()
38   - img.src = url
  44 + img.src = params.url
39 45 img.onload = () => {
40   - const ratio = img.width / img.height
41   - this.imageWidth = 800
42   - this.imageHeight = 800 / ratio
  46 + const imgScale = img.width / img.height
  47 + this.imageInfo.width = 800
  48 + this.imageInfo.height = 800 / imgScale
  49 + }
  50 + },
  51 + close() {
  52 + this.visible = false
  53 + this.imageInfo = {
  54 + url: '',
  55 + width: 800,
  56 + height: 800
43 57 }
44 58 },
45   - closeDialog() {
46   - this.dialogVisible = false
  59 + handleImageError(e) {
  60 + e.target.src = '/img/noPhoto.jpg'
47 61 }
48 62 }
49 63 }
50 64 </script>
51 65  
52 66 <style lang="scss" scoped>
53   -.image-viewer {
54   - .image-container {
55   - position: fixed;
56   - top: 50%;
57   - left: 50%;
58   - transform: translate(-50%, -50%);
59   - background-color: #fff;
60   - padding: 20px;
  67 +.view-image-container {
  68 + .image-viewer-dialog {
  69 + background-color: rgba(0, 0, 0, 0.8);
61 70  
62   - .close-icon {
63   - position: absolute;
64   - right: 20px;
65   - top: 20px;
66   - font-size: 24px;
67   - color: #F56C6C;
68   - cursor: pointer;
  71 + .image-wrapper {
  72 + position: relative;
  73 + display: flex;
  74 + justify-content: center;
  75 + align-items: center;
  76 + height: 100%;
  77 +
  78 + img {
  79 + max-width: 90%;
  80 + max-height: 90%;
  81 + object-fit: contain;
  82 + }
  83 +
  84 + .close-icon {
  85 + position: absolute;
  86 + top: 20px;
  87 + right: 20px;
  88 + font-size: 24px;
  89 + color: #fff;
  90 + cursor: pointer;
  91 + z-index: 2001;
  92 +
  93 + &:hover {
  94 + color: #f56c6c;
  95 + }
  96 + }
69 97 }
70 98 }
71 99 }
... ...
src/i18n/index.js
... ... @@ -143,6 +143,7 @@ import { messages as resourceI18n } from &#39;./resourceI18n&#39;
143 143 import { messages as carI18n } from './carI18n'
144 144 import { messages as scmI18n } from './scmI18n'
145 145 import { messages as userI18n } from './userI18n'
  146 +import { messages as systemI18n } from './systemI18n'
146 147  
147 148 Vue.use(VueI18n)
148 149  
... ... @@ -284,6 +285,7 @@ const messages = {
284 285 ...carI18n.en,
285 286 ...scmI18n.en,
286 287 ...userI18n.en,
  288 + ...systemI18n.en,
287 289 },
288 290 zh: {
289 291 ...loginMessages.zh,
... ... @@ -419,6 +421,7 @@ const messages = {
419 421 ...carI18n.zh,
420 422 ...scmI18n.zh,
421 423 ...userI18n.zh,
  424 + ...systemI18n.zh,
422 425 }
423 426 }
424 427  
... ...
src/i18n/systemI18n.js 0 → 100644
  1 +import { messages as communitySettingManageMessages } from '../views/system/communitySettingManageLang'
  2 +import { messages as storeInfoManageMessages } from '../views/system/storeInfoManageLang'
  3 +export const messages = {
  4 + en: {
  5 + ...communitySettingManageMessages.en,
  6 + ...storeInfoManageMessages.en,
  7 + },
  8 + zh: {
  9 + ...communitySettingManageMessages.zh,
  10 + ...storeInfoManageMessages.zh,
  11 + }
  12 +}
0 13 \ No newline at end of file
... ...
src/i18n/userI18n.js
... ... @@ -2,6 +2,12 @@ import { messages as staffCommunityMessages } from &#39;../views/staff/staffCommunit
2 2 import { messages as staffAppAuthManageMessages } from '../views/staff/staffAppAuthManageLang'
3 3 import { messages as dataPrivilegeManageMessages } from '../views/org/dataPrivilegeManageLang'
4 4 import { messages as classesManageMessages } from '../views/org/classesManageLang'
  5 +import { messages as scheduleClassesMessages } from '../views/org/scheduleClassesLang'
  6 +import { messages as addScheduleClassesMessages } from '../views/org/addScheduleClassesLang'
  7 +import { messages as editScheduleClassesMessages } from '../views/org/editScheduleClassesLang'
  8 +import { messages as scheduleClassesPageMessages } from '../views/org/scheduleClassesPageLang'
  9 +import { messages as workflowManageMessages } from '../views/system/workflowManageLang'
  10 +import { messages as workflowSettingManageMessages } from '../views/system/workflowSettingManageLang'
5 11  
6 12 export const messages = {
7 13 en: {
... ... @@ -9,11 +15,23 @@ export const messages = {
9 15 ...staffAppAuthManageMessages.en,
10 16 ...dataPrivilegeManageMessages.en,
11 17 ...classesManageMessages.en,
  18 + ...scheduleClassesMessages.en,
  19 + ...addScheduleClassesMessages.en,
  20 + ...editScheduleClassesMessages.en,
  21 + ...scheduleClassesPageMessages.en,
  22 + ...workflowManageMessages.en,
  23 + ...workflowSettingManageMessages.en,
12 24 },
13 25 zh: {
14 26 ...staffCommunityMessages.zh,
15 27 ...staffAppAuthManageMessages.zh,
16 28 ...dataPrivilegeManageMessages.zh,
17 29 ...classesManageMessages.zh,
  30 + ...scheduleClassesMessages.zh,
  31 + ...addScheduleClassesMessages.zh,
  32 + ...editScheduleClassesMessages.zh,
  33 + ...scheduleClassesPageMessages.zh,
  34 + ...workflowManageMessages.zh,
  35 + ...workflowSettingManageMessages.zh,
18 36 }
19 37 }
20 38 \ No newline at end of file
... ...
src/router/index.js
... ... @@ -13,6 +13,7 @@ import resourceRouter from &#39;./resourceRouter&#39;
13 13 import carRouter from './carRouter'
14 14 import scmRouter from './scmRouter'
15 15 import userRouter from './userRouter'
  16 +import systemRouter from './systemRouter'
16 17  
17 18 Vue.use(VueRouter)
18 19  
... ... @@ -634,6 +635,7 @@ const routes = [
634 635 ...carRouter,
635 636 ...scmRouter,
636 637 ...userRouter,
  638 + ...systemRouter,
637 639 // 其他子路由可以在这里添加
638 640 ]
639 641 },
... ...
src/router/systemRouter.js 0 → 100644
  1 +export default [
  2 + {
  3 + path: '/pages/property/communitySettingManage',
  4 + name: '/pages/property/communitySettingManage',
  5 + component: () => import('@/views/system/communitySettingManageList.vue')
  6 + },
  7 + {
  8 + path: '/pages/property/workflowManage',
  9 + name: '/pages/property/workflowManage',
  10 + component: () => import('@/views/system/workflowManageList.vue')
  11 + },
  12 + {
  13 + path: '/pages/property/workflowSettingManage',
  14 + name: '/pages/property/workflowSettingManage',
  15 + component: () => import('@/views/system/workflowSettingManageList.vue')
  16 + },
  17 + {
  18 + path:'/pages/common/storeInfoManage',
  19 + name:'/pages/common/storeInfoManage',
  20 + component: () => import('@/views/system/storeInfoManageList.vue')
  21 + },
  22 +]
0 23 \ No newline at end of file
... ...
src/router/userRouter.js
... ... @@ -15,8 +15,28 @@ export default [
15 15 component: () => import('@/views/org/dataPrivilegeManageList.vue')
16 16 },
17 17 {
18   - path:'/pages/property/classesManage',
19   - name:'/pages/property/classesManage',
  18 + path: '/pages/property/classesManage',
  19 + name: '/pages/property/classesManage',
20 20 component: () => import('@/views/org/classesManageList.vue')
  21 + },
  22 + {
  23 + path: '/pages/property/scheduleClasses',
  24 + name: '/pages/property/scheduleClasses',
  25 + component: () => import('@/views/org/scheduleClassesList.vue')
  26 + },
  27 + {
  28 + path: '/views/org/addScheduleClasses',
  29 + name: '/views/org/addScheduleClasses',
  30 + component: () => import('@/views/org/addScheduleClassesList.vue')
  31 + },
  32 + {
  33 + path: '/views/org/editScheduleClasses',
  34 + name: '/views/org/editScheduleClasses',
  35 + component: () => import('@/views/org/editScheduleClassesList.vue')
  36 + },
  37 + {
  38 + path:'/pages/property/scheduleClassesPage',
  39 + name:'/pages/property/scheduleClassesPage',
  40 + component: () => import('@/views/org/scheduleClassesPageList.vue')
21 41 },
22 42 ]
23 43 \ No newline at end of file
... ...
src/views/org/addScheduleClassesLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + addScheduleClasses: {
  4 + title: 'Add Schedule',
  5 + name: 'Schedule Name',
  6 + namePlaceholder: 'Required, please enter schedule name',
  7 + scheduleType: 'Schedule Type',
  8 + scheduleTypePlaceholder: 'Required, please select schedule type',
  9 + daily: 'Daily Schedule',
  10 + weekly: 'Weekly Schedule',
  11 + monthly: 'Monthly Schedule'
  12 + },
  13 + scheduleClassesDay: {
  14 + scheduleCycle: 'Schedule Cycle',
  15 + scheduleCyclePlaceholder: 'Required, please select schedule cycle',
  16 + day: 'Day',
  17 + scheduleInfo: 'Schedule Information',
  18 + rest: 'Rest'
  19 + },
  20 + scheduleClassesWeek: {
  21 + scheduleCycle: 'Schedule Cycle',
  22 + scheduleCyclePlaceholder: 'Required, please select schedule cycle',
  23 + week: 'Week',
  24 + scheduleInfo: 'Schedule Information',
  25 + rest: 'Rest'
  26 + },
  27 + scheduleClassesMonth: {
  28 + scheduleCycle: 'Schedule Cycle',
  29 + scheduleCyclePlaceholder: 'Required, please select schedule cycle',
  30 + month: 'Month',
  31 + scheduleInfo: 'Schedule Information',
  32 + rest: 'Rest'
  33 + },
  34 + editScheduleClassesDay: {
  35 + title: 'Edit Schedule',
  36 + status: 'Status',
  37 + rest: 'Rest',
  38 + startTime: 'Start Time',
  39 + startTimePlaceholder: 'Required, please enter start time',
  40 + endTime: 'End Time',
  41 + endTimePlaceholder: 'Required, please enter end time'
  42 + }
  43 + },
  44 + zh: {
  45 + addScheduleClasses: {
  46 + title: '添加排班',
  47 + name: '排班名称',
  48 + namePlaceholder: '必填,请填写计划名称',
  49 + scheduleType: '排班类型',
  50 + scheduleTypePlaceholder: '必填,请选择排班类型',
  51 + daily: '按日排班',
  52 + weekly: '按周排班',
  53 + monthly: '按月排班'
  54 + },
  55 + scheduleClassesDay: {
  56 + scheduleCycle: '排班周期',
  57 + scheduleCyclePlaceholder: '必填,请选择排班周期',
  58 + day: '日',
  59 + scheduleInfo: '排班信息',
  60 + rest: '休息'
  61 + },
  62 + scheduleClassesWeek: {
  63 + scheduleCycle: '排班周期',
  64 + scheduleCyclePlaceholder: '必填,请选择排班周期',
  65 + week: '周',
  66 + scheduleInfo: '排班信息',
  67 + rest: '休息'
  68 + },
  69 + scheduleClassesMonth: {
  70 + scheduleCycle: '排班周期',
  71 + scheduleCyclePlaceholder: '必填,请选择排班周期',
  72 + month: '月',
  73 + scheduleInfo: '排班信息',
  74 + rest: '休息'
  75 + },
  76 + editScheduleClassesDay: {
  77 + title: '修改',
  78 + status: '状态',
  79 + rest: '休息',
  80 + startTime: '上班时间',
  81 + startTimePlaceholder: '必填,请填写上班时间',
  82 + endTime: '下班时间',
  83 + endTimePlaceholder: '必填,请填写下班时间'
  84 + }
  85 + }
  86 +}
0 87 \ No newline at end of file
... ...
src/views/org/addScheduleClassesList.vue 0 → 100644
  1 +<template>
  2 + <div class="add-schedule-classes-container">
  3 + <el-row>
  4 + <el-col :span="24">
  5 + <el-card>
  6 + <div slot="header" class="clearfix">
  7 + <span>{{ $t('addScheduleClasses.title') }}</span>
  8 + </div>
  9 + <div>
  10 + <el-form label-position="right" label-width="120px">
  11 + <el-form-item :label="$t('addScheduleClasses.name')">
  12 + <el-input v-model="addScheduleClassesInfo.name"
  13 + :placeholder="$t('addScheduleClasses.namePlaceholder')" />
  14 + </el-form-item>
  15 + <el-form-item :label="$t('addScheduleClasses.scheduleType')">
  16 + <el-select v-model="addScheduleClassesInfo.scheduleType"
  17 + :placeholder="$t('addScheduleClasses.scheduleTypePlaceholder')" @change="changeScheduleType"
  18 + style="width:100%">
  19 + <el-option :label="$t('addScheduleClasses.daily')" value="1001" />
  20 + <el-option :label="$t('addScheduleClasses.weekly')" value="2002" />
  21 + <el-option :label="$t('addScheduleClasses.monthly')" value="3003" />
  22 + </el-select>
  23 + </el-form-item>
  24 +
  25 + <div v-if="addScheduleClassesInfo.scheduleType === '1001'">
  26 + <schedule-classes-day ref="scheduleDay" @editDay="editDay" />
  27 + </div>
  28 + <div v-if="addScheduleClassesInfo.scheduleType === '2002'">
  29 + <schedule-classes-week ref="scheduleWeek" @editDay="editDay" />
  30 + </div>
  31 + <div v-if="addScheduleClassesInfo.scheduleType === '3003'">
  32 + <schedule-classes-month ref="scheduleMonth" @editDay="editDay" />
  33 + </div>
  34 +
  35 + <div class="action-buttons">
  36 + <el-button type="primary" @click="saveScheduleClassesInfo">
  37 + {{ $t('common.save') }}
  38 + </el-button>
  39 + <el-button @click="goBack">
  40 + {{ $t('common.back') }}
  41 + </el-button>
  42 + </div>
  43 + </el-form>
  44 + </div>
  45 + </el-card>
  46 + </el-col>
  47 + </el-row>
  48 +
  49 + <edit-schedule-classes-day ref="editScheduleDay" @update="updateDay" />
  50 + </div>
  51 +</template>
  52 +
  53 +<script>
  54 +import ScheduleClassesDay from '@/components/org/scheduleClassesDay'
  55 +import ScheduleClassesWeek from '@/components/org/scheduleClassesWeek'
  56 +import ScheduleClassesMonth from '@/components/org/scheduleClassesMonth'
  57 +import EditScheduleClassesDay from '@/components/org/editScheduleClassesDay'
  58 +import { saveScheduleClasses } from '@/api/org/addScheduleClassesApi'
  59 +
  60 +export default {
  61 + name: 'AddScheduleClassesList',
  62 + components: {
  63 + ScheduleClassesDay,
  64 + ScheduleClassesWeek,
  65 + ScheduleClassesMonth,
  66 + EditScheduleClassesDay
  67 + },
  68 + data() {
  69 + return {
  70 + addScheduleClassesInfo: {
  71 + name: '',
  72 + scheduleType: '',
  73 + scheduleCycle: '1',
  74 + days: []
  75 + }
  76 + }
  77 + },
  78 + methods: {
  79 + changeScheduleType() {
  80 + this.addScheduleClassesInfo.days = []
  81 + this.addScheduleClassesInfo.scheduleCycle = 1
  82 +
  83 + if (this.addScheduleClassesInfo.scheduleType === '1001') {
  84 + this.$refs.scheduleDay.initData(this.addScheduleClassesInfo.scheduleCycle)
  85 + } else if (this.addScheduleClassesInfo.scheduleType === '2002') {
  86 + this.$refs.scheduleWeek.initData(this.addScheduleClassesInfo.scheduleCycle)
  87 + } else if (this.addScheduleClassesInfo.scheduleType === '3003') {
  88 + this.$refs.scheduleMonth.initData(this.addScheduleClassesInfo.scheduleCycle)
  89 + }
  90 + },
  91 + async saveScheduleClassesInfo() {
  92 + try {
  93 + // 根据不同类型获取数据
  94 + if (this.addScheduleClassesInfo.scheduleType === '1001') {
  95 + this.addScheduleClassesInfo.days = this.$refs.scheduleDay.getDaysData()
  96 + } else if (this.addScheduleClassesInfo.scheduleType === '2002') {
  97 + this.addScheduleClassesInfo.days = this.$refs.scheduleWeek.getDaysData()
  98 + } else if (this.addScheduleClassesInfo.scheduleType === '3003') {
  99 + this.addScheduleClassesInfo.days = this.$refs.scheduleMonth.getDaysData()
  100 + }
  101 +
  102 + const res = await saveScheduleClasses(this.addScheduleClassesInfo)
  103 + if (res.code === 0) {
  104 + this.$message.success(this.$t('common.saveSuccess'))
  105 + this.goBack()
  106 + } else {
  107 + this.$message.error(res.msg)
  108 + }
  109 + } catch (error) {
  110 + this.$message.error(this.$t('common.saveFailed'))
  111 + }
  112 + },
  113 + goBack() {
  114 + this.$router.go(-1)
  115 + },
  116 + editDay(item) {
  117 + this.$refs.editScheduleDay.open(item)
  118 + },
  119 + updateDay(updatedItem) {
  120 + // 由于 editScheduleClassesDay 组件现在直接修改传入的对象
  121 + // 这里不需要额外的处理,但保留方法以保持代码完整性
  122 + console.log('Day updated:', updatedItem)
  123 + }
  124 + }
  125 +}
  126 +</script>
  127 +
  128 +<style lang="scss" scoped>
  129 +.add-schedule-classes-container {
  130 + padding: 20px;
  131 +
  132 + .action-buttons {
  133 + margin-top: 20px;
  134 + text-align: right;
  135 +
  136 + .el-button+.el-button {
  137 + margin-left: 10px;
  138 + }
  139 + }
  140 +}
  141 +</style>
0 142 \ No newline at end of file
... ...
src/views/org/editScheduleClassesLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + editScheduleClasses: {
  4 + title: 'Edit Schedule',
  5 + name: 'Schedule Name',
  6 + namePlaceholder: 'Required, please enter schedule name',
  7 + scheduleType: 'Schedule Type',
  8 + scheduleTypePlaceholder: 'Required, please select schedule type',
  9 + daySchedule: 'Daily Schedule',
  10 + weekSchedule: 'Weekly Schedule',
  11 + monthSchedule: 'Monthly Schedule',
  12 + rest: 'Rest'
  13 + },
  14 + scheduleClassesDay: {
  15 + scheduleCycle: 'Schedule Cycle',
  16 + scheduleCyclePlaceholder: 'Required, please select schedule cycle',
  17 + day: 'Day',
  18 + scheduleInfo: 'Schedule Information',
  19 + rest: 'Rest'
  20 + },
  21 + scheduleClassesWeek: {
  22 + scheduleCycle: 'Schedule Cycle',
  23 + scheduleCyclePlaceholder: 'Required, please select schedule cycle',
  24 + week: 'Week',
  25 + scheduleInfo: 'Schedule Information',
  26 + rest: 'Rest'
  27 + },
  28 + scheduleClassesMonth: {
  29 + scheduleCycle: 'Schedule Cycle',
  30 + scheduleCyclePlaceholder: 'Required, please select schedule cycle',
  31 + month: 'Month',
  32 + scheduleInfo: 'Schedule Information',
  33 + rest: 'Rest'
  34 + },
  35 + editScheduleClassesDay: {
  36 + editTitle: 'Edit Schedule Day',
  37 + status: 'Status',
  38 + startTime: 'Start Time',
  39 + startTimePlaceholder: 'Required, please enter start time',
  40 + endTime: 'End Time',
  41 + endTimePlaceholder: 'Required, please enter end time',
  42 + rest: 'Rest'
  43 + }
  44 + },
  45 + zh: {
  46 + editScheduleClasses: {
  47 + title: '修改排班',
  48 + name: '排班名称',
  49 + namePlaceholder: '必填,请填写计划名称',
  50 + scheduleType: '排班类型',
  51 + scheduleTypePlaceholder: '必填,请选择排班类型',
  52 + daySchedule: '按日排班',
  53 + weekSchedule: '按周排班',
  54 + monthSchedule: '按月排班',
  55 + rest: '休息'
  56 + },
  57 + scheduleClassesDay: {
  58 + scheduleCycle: '排班周期',
  59 + scheduleCyclePlaceholder: '必填,请选择排班周期',
  60 + day: '日',
  61 + scheduleInfo: '排班信息',
  62 + rest: '休息'
  63 + },
  64 + scheduleClassesWeek: {
  65 + scheduleCycle: '排班周期',
  66 + scheduleCyclePlaceholder: '必填,请选择排班周期',
  67 + week: '周',
  68 + scheduleInfo: '排班信息',
  69 + rest: '休息'
  70 + },
  71 + scheduleClassesMonth: {
  72 + scheduleCycle: '排班周期',
  73 + scheduleCyclePlaceholder: '必填,请选择排班周期',
  74 + month: '月',
  75 + scheduleInfo: '排班信息',
  76 + rest: '休息'
  77 + },
  78 + editScheduleClassesDay: {
  79 + editTitle: '修改',
  80 + status: '状态',
  81 + startTime: '上班时间',
  82 + startTimePlaceholder: '必填,请填写上班时间',
  83 + endTime: '下班时间',
  84 + endTimePlaceholder: '必填,请填写下班时间',
  85 + rest: '休息'
  86 + }
  87 + }
  88 +}
0 89 \ No newline at end of file
... ...
src/views/org/editScheduleClassesList.vue 0 → 100644
  1 +<template>
  2 + <div class="edit-schedule-classes-container">
  3 + <el-card class="box-card">
  4 + <div slot="header" class="clearfix">
  5 + <h5>{{ $t('editScheduleClasses.title') }}</h5>
  6 + </div>
  7 + <el-row :gutter="20">
  8 + <el-col :span="24">
  9 + <div class="form-content">
  10 + <el-form ref="form" :model="editScheduleClassesInfo" label-width="120px">
  11 + <el-form-item :label="$t('editScheduleClasses.name')">
  12 + <el-input v-model="editScheduleClassesInfo.name"
  13 + :placeholder="$t('editScheduleClasses.namePlaceholder')" clearable />
  14 + </el-form-item>
  15 + <el-form-item :label="$t('editScheduleClasses.scheduleType')">
  16 + <el-select v-model="editScheduleClassesInfo.scheduleType"
  17 + :placeholder="$t('editScheduleClasses.scheduleTypePlaceholder')" style="width:100%"
  18 + @change="changeScheduleType(1)">
  19 + <el-option disabled :value="''" :label="$t('editScheduleClasses.scheduleTypePlaceholder')" />
  20 + <el-option value="1001" :label="$t('editScheduleClasses.daySchedule')" />
  21 + <el-option value="2002" :label="$t('editScheduleClasses.weekSchedule')" />
  22 + <el-option value="3003" :label="$t('editScheduleClasses.monthSchedule')" />
  23 + </el-select>
  24 + </el-form-item>
  25 +
  26 + <div v-if="editScheduleClassesInfo.scheduleType === '1001'">
  27 + <schedule-classes-day ref="dayComponent" :schedule-cycle="editScheduleClassesInfo.scheduleCycle"
  28 + :days="editScheduleClassesInfo.days" @editDay="editDay" />
  29 + </div>
  30 + <div v-if="editScheduleClassesInfo.scheduleType === '2002'">
  31 + <schedule-classes-week ref="weekComponent" :schedule-cycle="editScheduleClassesInfo.scheduleCycle"
  32 + :days="editScheduleClassesInfo.days" @editDay="editDay" />
  33 + </div>
  34 + <div v-if="editScheduleClassesInfo.scheduleType === '3003'">
  35 + <schedule-classes-month ref="monthComponent" :schedule-cycle="editScheduleClassesInfo.scheduleCycle"
  36 + :days="editScheduleClassesInfo.days" @editDay="editDay" />
  37 + </div>
  38 +
  39 + <el-form-item>
  40 + <el-button type="primary" @click="editScheduleClasses">
  41 + <i class="el-icon-check" /> {{ $t('common.save') }}
  42 + </el-button>
  43 + <el-button type="warning" style="margin-right:20px;" @click="goBack">
  44 + <i class="el-icon-close" /> {{ $t('common.back') }}
  45 + </el-button>
  46 + </el-form-item>
  47 + </el-form>
  48 + </div>
  49 + </el-col>
  50 + </el-row>
  51 + </el-card>
  52 +
  53 + <edit-schedule-classes-day ref="editDayDialog" />
  54 + </div>
  55 +</template>
  56 +
  57 +<script>
  58 +import ScheduleClassesDay from '@/components/org/scheduleClassesDay'
  59 +import ScheduleClassesWeek from '@/components/org/scheduleClassesWeek'
  60 +import ScheduleClassesMonth from '@/components/org/scheduleClassesMonth'
  61 +import EditScheduleClassesDay from '@/components/org/editScheduleClassesDay'
  62 +import { updateScheduleClasses, listScheduleClasses, listScheduleClassesDay } from '@/api/org/editScheduleClassesApi'
  63 +
  64 +export default {
  65 + name: 'EditScheduleClassesList',
  66 + components: {
  67 + ScheduleClassesDay,
  68 + ScheduleClassesWeek,
  69 + ScheduleClassesMonth,
  70 + EditScheduleClassesDay
  71 + },
  72 + data() {
  73 + return {
  74 + editScheduleClassesInfo: {
  75 + scheduleId: '',
  76 + name: '',
  77 + scheduleType: '',
  78 + scheduleCycle: '1',
  79 + days: []
  80 + }
  81 + }
  82 + },
  83 + created() {
  84 + this.editScheduleClassesInfo.scheduleId = this.$route.query.scheduleId
  85 + this.listScheduleClassess()
  86 + this.listScheduleClassesDays()
  87 + },
  88 + methods: {
  89 + changeScheduleType(customChange) {
  90 + if (customChange) {
  91 + this.editScheduleClassesInfo.days = []
  92 + this.editScheduleClassesInfo.scheduleCycle = 1
  93 + }
  94 +
  95 + if (this.editScheduleClassesInfo.scheduleType === '1001') {
  96 + setTimeout(() => {
  97 + this.$refs.dayComponent.notify({
  98 + scheduleCycle: this.editScheduleClassesInfo.scheduleCycle,
  99 + days: this.editScheduleClassesInfo.days
  100 + })
  101 + }, 500)
  102 + }
  103 + if (this.editScheduleClassesInfo.scheduleType === '2002') {
  104 + setTimeout(() => {
  105 + this.$refs.weekComponent.notify({
  106 + scheduleCycle: this.editScheduleClassesInfo.scheduleCycle,
  107 + days: this.editScheduleClassesInfo.days
  108 + })
  109 + }, 100)
  110 + }
  111 + if (this.editScheduleClassesInfo.scheduleType === '3003') {
  112 + setTimeout(() => {
  113 + this.$refs.monthComponent.notify({
  114 + scheduleCycle: this.editScheduleClassesInfo.scheduleCycle,
  115 + days: this.editScheduleClassesInfo.days
  116 + })
  117 + }, 100)
  118 + }
  119 + },
  120 + async editScheduleClasses() {
  121 + try {
  122 + await updateScheduleClasses(this.editScheduleClassesInfo)
  123 + this.$message.success(this.$t('common.updateSuccess'))
  124 + this.goBack()
  125 + } catch (error) {
  126 + this.$message.error(error.message || this.$t('common.updateFailed'))
  127 + }
  128 + },
  129 + async listScheduleClassess() {
  130 + try {
  131 + const { data } = await listScheduleClasses({
  132 + page: 1,
  133 + row: 1,
  134 + scheduleId: this.editScheduleClassesInfo.scheduleId
  135 + })
  136 + Object.assign(this.editScheduleClassesInfo, data[0])
  137 + this.changeScheduleType()
  138 + } catch (error) {
  139 + console.error('请求失败:', error)
  140 + }
  141 + },
  142 + async listScheduleClassesDays() {
  143 + try {
  144 + const { data } = await listScheduleClassesDay({
  145 + page: 1,
  146 + row: 100,
  147 + scheduleId: this.editScheduleClassesInfo.scheduleId
  148 + })
  149 + data.forEach(item => {
  150 + if (item.workday === '2002') {
  151 + item.workdayName = this.$t('editScheduleClasses.rest')
  152 + }
  153 + })
  154 + this.editScheduleClassesInfo.days = data
  155 + this.changeScheduleType()
  156 + } catch (error) {
  157 + console.error('请求失败:', error)
  158 + }
  159 + },
  160 + goBack() {
  161 + this.$router.go(-1)
  162 + },
  163 + editDay(item) {
  164 + this.$refs.editDayDialog.open(item)
  165 + }
  166 + }
  167 +}
  168 +</script>
  169 +
  170 +<style lang="scss" scoped>
  171 +.edit-schedule-classes-container {
  172 + padding: 20px;
  173 +
  174 + .box-card {
  175 + margin-bottom: 20px;
  176 + }
  177 +
  178 + .form-content {
  179 + padding: 20px;
  180 + }
  181 +
  182 + .el-form-item {
  183 + margin-bottom: 22px;
  184 + }
  185 +}
  186 +</style>
0 187 \ No newline at end of file
... ...
src/views/org/scheduleClassesLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + scheduleClasses: {
  4 + search: {
  5 + title: 'Search Conditions',
  6 + nameLike: 'Please enter schedule name',
  7 + state: 'Please select status',
  8 + allState: 'All Status'
  9 + },
  10 + list: {
  11 + title: 'Schedule Settings'
  12 + },
  13 + table: {
  14 + name: 'Schedule Name',
  15 + scheduleType: 'Schedule Type',
  16 + scheduleCycle: 'Schedule Cycle',
  17 + computeTime: 'Effective Time',
  18 + staff: 'Staff',
  19 + staffLink: 'Staff',
  20 + state: 'Status',
  21 + createTime: 'Create Time'
  22 + },
  23 + delete: {
  24 + title: 'Confirm Operation',
  25 + confirm: 'Are you sure to delete this schedule?',
  26 + success: 'Delete successfully',
  27 + error: 'Delete failed'
  28 + },
  29 + state: {
  30 + title: 'Confirm Operation',
  31 + confirm: 'Are you sure to',
  32 + schedule: 'this schedule?',
  33 + success: 'Operation successfully',
  34 + error: 'Operation failed'
  35 + },
  36 + fetchError: 'Failed to fetch schedule data'
  37 + }
  38 + },
  39 + zh: {
  40 + scheduleClasses: {
  41 + search: {
  42 + title: '查询条件',
  43 + nameLike: '请输入排班名称',
  44 + state: '请选择状态',
  45 + allState: '全部状态'
  46 + },
  47 + list: {
  48 + title: '排班设置'
  49 + },
  50 + table: {
  51 + name: '班次名称',
  52 + scheduleType: '排班类型',
  53 + scheduleCycle: '排班周期',
  54 + computeTime: '生效时间',
  55 + staff: '人员',
  56 + staffLink: '人员',
  57 + state: '状态',
  58 + createTime: '创建时间'
  59 + },
  60 + delete: {
  61 + title: '确认操作',
  62 + confirm: '确定删除排班吗?',
  63 + success: '删除成功',
  64 + error: '删除失败'
  65 + },
  66 + state: {
  67 + title: '确认操作',
  68 + confirm: '确定',
  69 + schedule: '该排班吗?',
  70 + success: '操作成功',
  71 + error: '操作失败'
  72 + },
  73 + fetchError: '获取排班数据失败'
  74 + }
  75 + }
  76 +}
0 77 \ No newline at end of file
... ...
src/views/org/scheduleClassesList.vue 0 → 100644
  1 +<template>
  2 + <div class="schedule-classes-container animated fadeInRight">
  3 + <!-- 查询条件 -->
  4 + <el-card class="search-wrapper">
  5 + <div slot="header" class="text-left">
  6 + <span>{{ $t('scheduleClasses.search.title') }}</span>
  7 + </div>
  8 + <el-row :gutter="20">
  9 + <el-col :span="6">
  10 + <el-input v-model="searchForm.nameLike" :placeholder="$t('scheduleClasses.search.nameLike')" clearable
  11 + @keyup.enter.native="handleSearch" />
  12 + </el-col>
  13 + <el-col :span="6">
  14 + <el-select v-model="searchForm.state" :placeholder="$t('scheduleClasses.search.state')" style="width:100%">
  15 + <el-option :label="$t('scheduleClasses.search.allState')" value="" />
  16 + <el-option v-for="item in states" :key="item.statusCd" :label="item.name" :value="item.statusCd" />
  17 + </el-select>
  18 + </el-col>
  19 + <el-col :span="4">
  20 + <el-button type="primary" @click="handleSearch">
  21 + <i class="el-icon-search"></i>
  22 + {{ $t('common.search') }}
  23 + </el-button>
  24 + </el-col>
  25 + </el-row>
  26 + </el-card>
  27 +
  28 + <!-- 排班设置 -->
  29 + <el-card class="list-wrapper">
  30 + <div slot="header" class="flex justify-between ">
  31 + <span>{{ $t('scheduleClasses.list.title') }}</span>
  32 + <el-button type="primary" style="float: right;" @click="handleAdd">
  33 + <i class="el-icon-plus"></i>
  34 + {{ $t('common.add') }}
  35 + </el-button>
  36 + </div>
  37 +
  38 + <el-table v-loading="loading" :data="tableData" border style="width: 100%">
  39 + <el-table-column prop="name" :label="$t('scheduleClasses.table.name')" align="center" />
  40 + <el-table-column prop="scheduleTypeName" :label="$t('scheduleClasses.table.scheduleType')" align="center" />
  41 + <el-table-column prop="scheduleCycle" :label="$t('scheduleClasses.table.scheduleCycle')" align="center" />
  42 + <el-table-column prop="computeTime" :label="$t('scheduleClasses.table.computeTime')" align="center" />
  43 + <el-table-column :label="$t('scheduleClasses.table.staff')" align="center">
  44 + <template slot-scope="scope">
  45 + {{ scope.row.staffCount }} (
  46 + <el-link type="primary" @click="handleStaff(scope.row)">
  47 + {{ $t('scheduleClasses.table.staffLink') }}
  48 + </el-link>
  49 + )
  50 + </template>
  51 + </el-table-column>
  52 + <el-table-column prop="stateName" :label="$t('scheduleClasses.table.state')" align="center" />
  53 + <el-table-column prop="createTime" :label="$t('scheduleClasses.table.createTime')" align="center" />
  54 + <el-table-column :label="$t('common.operation')" align="center" width="300">
  55 + <template slot-scope="scope">
  56 + <el-button size="mini" type="primary" @click="handleEdit(scope.row)">
  57 + {{ $t('common.edit') }}
  58 + </el-button>
  59 + <el-button size="mini" type="danger" @click="handleDelete(scope.row)">
  60 + {{ $t('common.delete') }}
  61 + </el-button>
  62 + <el-button v-if="scope.row.state === '1001'" size="mini" type="warning" @click="handleDisable(scope.row)">
  63 + {{ $t('common.disabled') }}
  64 + </el-button>
  65 + <el-button v-else size="mini" type="success" @click="handleEnable(scope.row)">
  66 + {{ $t('common.enabled') }}
  67 + </el-button>
  68 + </template>
  69 + </el-table-column>
  70 + </el-table>
  71 +
  72 + <el-pagination :current-page.sync="page.current" :page-sizes="[10, 20, 30, 50]" :page-size="page.size"
  73 + :total="page.total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange"
  74 + @current-change="handleCurrentChange" />
  75 + </el-card>
  76 +
  77 + <!-- 删除对话框 -->
  78 + <delete-schedule-classes ref="deleteDialog" @success="handleSuccess" />
  79 +
  80 + <!-- 状态对话框 -->
  81 + <schedule-classes-state ref="stateDialog" @success="handleSuccess" />
  82 + </div>
  83 +</template>
  84 +
  85 +<script>
  86 +import { listScheduleClasses } from '@/api/org/scheduleClassesApi'
  87 +import {getDict} from '@/api/community/communityApi'
  88 +import DeleteScheduleClasses from '@/components/org/deleteScheduleClasses'
  89 +import ScheduleClassesState from '@/components/org/scheduleClassesState'
  90 +
  91 +export default {
  92 + name: 'ScheduleClassesList',
  93 + components: {
  94 + DeleteScheduleClasses,
  95 + ScheduleClassesState
  96 + },
  97 + data() {
  98 + return {
  99 + loading: false,
  100 + searchForm: {
  101 + nameLike: '',
  102 + state: ''
  103 + },
  104 + states: [],
  105 + tableData: [],
  106 + page: {
  107 + current: 1,
  108 + size: 10,
  109 + total: 0
  110 + }
  111 + }
  112 + },
  113 + created() {
  114 + this.getList()
  115 + this.getDictData()
  116 + },
  117 + methods: {
  118 + async getList() {
  119 + try {
  120 + this.loading = true
  121 + const params = {
  122 + page: this.page.current,
  123 + row: this.page.size,
  124 + ...this.searchForm
  125 + }
  126 + const { data, total } = await listScheduleClasses(params)
  127 + this.tableData = data
  128 + this.page.total = total
  129 + } catch (error) {
  130 + this.$message.error(this.$t('scheduleClasses.fetchError'))
  131 + } finally {
  132 + this.loading = false
  133 + }
  134 + },
  135 + async getDictData() {
  136 + try {
  137 + const data = await getDict('schedule_classes', 'state')
  138 + this.states = data
  139 + } catch (error) {
  140 + console.error('获取字典数据失败:', error)
  141 + }
  142 + },
  143 + handleSearch() {
  144 + this.page.current = 1
  145 + this.getList()
  146 + },
  147 + handleAdd() {
  148 + this.$router.push('/views/org/addScheduleClasses')
  149 + },
  150 + handleEdit(row) {
  151 + this.$router.push(`/views/org/editScheduleClasses?scheduleId=${row.scheduleId}`)
  152 + },
  153 + handleDelete(row) {
  154 + this.$refs.deleteDialog.open(row)
  155 + },
  156 + handleEnable(row) {
  157 + this.$refs.stateDialog.open({
  158 + scheduleId: row.scheduleId,
  159 + stateName: this.$t('common.enable'),
  160 + state: '1001'
  161 + })
  162 + },
  163 + handleDisable(row) {
  164 + this.$refs.stateDialog.open({
  165 + scheduleId: row.scheduleId,
  166 + stateName: this.$t('common.disable'),
  167 + state: '2002'
  168 + })
  169 + },
  170 + handleStaff(row) {
  171 + this.$router.push(`/org/scheduleClassesStaffManage?scheduleId=${row.scheduleId}`)
  172 + },
  173 + handleSuccess() {
  174 + this.getList()
  175 + },
  176 + handleSizeChange(val) {
  177 + this.page.size = val
  178 + this.getList()
  179 + },
  180 + handleCurrentChange(val) {
  181 + this.page.current = val
  182 + this.getList()
  183 + }
  184 + }
  185 +}
  186 +</script>
  187 +
  188 +<style lang="scss" scoped>
  189 +.schedule-classes-container {
  190 + padding: 20px;
  191 +
  192 + .search-wrapper {
  193 + margin-bottom: 20px;
  194 + }
  195 +
  196 + .list-wrapper {
  197 + margin-bottom: 20px;
  198 + }
  199 +
  200 + .el-pagination {
  201 + margin-top: 20px;
  202 + text-align: right;
  203 + }
  204 +}
  205 +</style>
0 206 \ No newline at end of file
... ...
src/views/org/scheduleClassesPageLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + scheduleClassesPage: {
  4 + searchConditions: 'Search Conditions',
  5 + scheduleTable: 'Schedule Table',
  6 + staffNamePlaceholder: 'Please enter staff name',
  7 + schedulePlaceholder: 'Please select schedule',
  8 + selectSchedule: 'Select Schedule',
  9 + datePlaceholder: 'Please enter date YYYY-MM',
  10 + orgPlaceholder: 'Required, please select organization',
  11 + staffName: 'Staff Name',
  12 + day: 'Day',
  13 + rest: 'Rest',
  14 + selectOrg: 'Select Organization'
  15 + }
  16 + },
  17 + zh: {
  18 + scheduleClassesPage: {
  19 + searchConditions: '查询条件',
  20 + scheduleTable: '排班表',
  21 + staffNamePlaceholder: '请输入员工名称',
  22 + schedulePlaceholder: '请选择班次',
  23 + selectSchedule: '请选择班次',
  24 + datePlaceholder: '请输入日期 YYYY-MM',
  25 + orgPlaceholder: '必填,请选择关联组织',
  26 + staffName: '员工名称',
  27 + day: '日',
  28 + rest: '休息',
  29 + selectOrg: '选择组织'
  30 + }
  31 + }
  32 +}
0 33 \ No newline at end of file
... ...
src/views/org/scheduleClassesPageList.vue 0 → 100644
  1 +<template>
  2 + <div class="schedule-classes-container animated fadeInRight">
  3 + <!-- 查询条件 -->
  4 + <el-card class="box-card">
  5 + <div slot="header" class="flex justify-between">
  6 + <span>{{ $t('scheduleClassesPage.searchConditions') }}</span>
  7 + <el-button style="float: right; padding: 3px 0" type="text" @click="_moreCondition()">
  8 + {{ scheduleClassesPageInfo.moreCondition ? $t('common.hide') : $t('common.more') }}
  9 + </el-button>
  10 + </div>
  11 + <el-row :gutter="20">
  12 + <el-col :span="6">
  13 + <el-input v-model="scheduleClassesPageInfo.conditions.staffNameLike"
  14 + :placeholder="$t('scheduleClassesPage.staffNamePlaceholder')" clearable />
  15 + </el-col>
  16 + <el-col :span="8">
  17 + <el-select v-model="scheduleClassesPageInfo.conditions.scheduleId" style="width:100%"
  18 + :placeholder="$t('scheduleClassesPage.schedulePlaceholder')">
  19 + <el-option :label="$t('scheduleClassesPage.selectSchedule')" value="" />
  20 + <el-option v-for="(item, index) in scheduleClassesPageInfo.scheduleClassess" :key="index" :label="item.name"
  21 + :value="item.scheduleId" />
  22 + </el-select>
  23 + </el-col>
  24 + <el-col :span="6">
  25 + <el-date-picker v-model="scheduleClassesPageInfo.conditions.curDate" type="month"
  26 + :placeholder="$t('scheduleClassesPage.datePlaceholder')" format="yyyy-MM" value-format="yyyy-MM"
  27 + style="width:100%" @change="handleDateChange" />
  28 + </el-col>
  29 + <el-col :span="4">
  30 + <el-button type="primary" @click="_queryScheduleClassesMethod()">
  31 + {{ $t('common.search') }}
  32 + </el-button>
  33 + <el-button type="primary" @click="_exportScheduleClasses()">
  34 + <i class="el-icon-upload" />
  35 + {{ $t('common.export') }}
  36 + </el-button>
  37 + </el-col>
  38 + </el-row>
  39 +
  40 + <el-row v-show="scheduleClassesPageInfo.moreCondition" :gutter="20" style="margin-top:15px">
  41 + <el-col :span="6">
  42 + <el-input v-model="scheduleClassesPageInfo.conditions.orgName" readonly
  43 + :placeholder="$t('scheduleClassesPage.orgPlaceholder')" @focus="_staffChangeOrg()" />
  44 + </el-col>
  45 + </el-row>
  46 + </el-card>
  47 +
  48 + <!-- 排班表 -->
  49 + <el-card class="box-card" style="margin-top:20px">
  50 + <div slot="header" class="flex justify-between">
  51 + <span>{{ $t('scheduleClassesPage.scheduleTable') }}</span>
  52 + </div>
  53 + <div class="hc-table-div" :style="{ width: _computeTableDivWidth() }">
  54 + <el-table :data="scheduleClassesPageInfo.staffs" border style="width: 100%">
  55 + <el-table-column prop="staffName" :label="$t('scheduleClassesPage.staffName')" align="center" width="120" />
  56 + <el-table-column v-for="index in scheduleClassesPageInfo.maxDay" :key="index"
  57 + :label="`${index}${$t('scheduleClassesPage.day')}`" align="center">
  58 + <template slot-scope="scope">
  59 + <div v-for="(day, dayIndex) in scope.row.days" :key="dayIndex"
  60 + class="text-center border padding-lg labeling-strip" style="border-radius: 5px;cursor:pointer">
  61 + <div>{{ day.workdayName || $t('scheduleClassesPage.rest') }}</div>
  62 + <div v-for="(time, timeIndex) in day.times" :key="timeIndex">
  63 + {{ time.startTime }}-{{ time.endTime }}
  64 + </div>
  65 + </div>
  66 + </template>
  67 + </el-table-column>
  68 + </el-table>
  69 + </div>
  70 +
  71 + <!-- 分页 -->
  72 + <el-pagination :current-page.sync="page.current" :page-sizes="[10, 20, 30, 50]" :page-size="page.size"
  73 + :total="page.total" layout="total, sizes, prev, pager, next, jumper" style="margin-top:20px;text-align:right"
  74 + @size-change="handleSizeChange" @current-change="handleCurrentChange" />
  75 + </el-card>
  76 +
  77 + <!-- 选择组织弹窗 -->
  78 + <choose-org-tree ref="chooseOrgTree" @switchOrg="handleSwitchOrg" />
  79 + </div>
  80 +</template>
  81 +
  82 +<script>
  83 +import { getCommunityId } from '@/api/community/communityApi'
  84 +import { listScheduleClasses, staffMonthScheduleClasses, exportData } from '@/api/org/scheduleClassesPageApi'
  85 +import ChooseOrgTree from '@/components/org/ChooseOrgTree'
  86 +
  87 +export default {
  88 + name: 'ScheduleClassesPageList',
  89 + components: {
  90 + ChooseOrgTree
  91 + },
  92 + data() {
  93 + return {
  94 + communityId: '',
  95 + scheduleClassesPageInfo: {
  96 + staffs: [],
  97 + scheduleClassess: [],
  98 + maxDay: 30,
  99 + curMonth: '',
  100 + curYear: '',
  101 + total: 0,
  102 + records: 1,
  103 + moreCondition: false,
  104 + conditions: {
  105 + staffNameLike: '',
  106 + scheduleId: '',
  107 + curDate: '',
  108 + orgId: '',
  109 + orgName: ''
  110 + }
  111 + },
  112 + page: {
  113 + current: 1,
  114 + size: 10,
  115 + total: 0
  116 + }
  117 + }
  118 + },
  119 + created() {
  120 + this.communityId = getCommunityId()
  121 + this.initStaffDate()
  122 + this._listScheduleClassess()
  123 + this._listStaffScheduleClassess(this.page.current, this.page.size)
  124 + },
  125 + methods: {
  126 + initStaffDate() {
  127 + const date = new Date()
  128 + this.scheduleClassesPageInfo.curMonth = date.getMonth() + 1
  129 + this.scheduleClassesPageInfo.curYear = date.getFullYear()
  130 + this.scheduleClassesPageInfo.conditions.curDate = `${date.getFullYear()}-${date.getMonth() + 1}`
  131 + this.scheduleClassesPageInfo.maxDay = new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate()
  132 + },
  133 + handleDateChange(val) {
  134 + if (val) {
  135 + const values = val.split('-')
  136 + this.scheduleClassesPageInfo.curYear = values[0]
  137 + this.scheduleClassesPageInfo.curMonth = values[1]
  138 + this._listStaffScheduleClassess(this.page.current, this.page.size)
  139 + }
  140 + },
  141 + async _listStaffScheduleClassess(page, size) {
  142 + try {
  143 + const params = {
  144 + ...this.scheduleClassesPageInfo.conditions,
  145 + page,
  146 + row: size,
  147 + communityId: this.communityId
  148 + }
  149 + const { data, total, records } = await staffMonthScheduleClasses(params)
  150 + this.scheduleClassesPageInfo.staffs = data
  151 + this.scheduleClassesPageInfo.total = total
  152 + this.scheduleClassesPageInfo.records = records
  153 + this.page.total = total
  154 + } catch (error) {
  155 + console.error('获取员工排班表失败:', error)
  156 + }
  157 + },
  158 + async _listScheduleClassess() {
  159 + try {
  160 + const params = {
  161 + page: 1,
  162 + row: 100,
  163 + communityId: this.communityId
  164 + }
  165 + const { data } = await listScheduleClasses(params)
  166 + this.scheduleClassesPageInfo.scheduleClassess = data
  167 + } catch (error) {
  168 + console.error('获取班次列表失败:', error)
  169 + }
  170 + },
  171 + _queryScheduleClassesMethod() {
  172 + this.page.current = 1
  173 + this._listStaffScheduleClassess(this.page.current, this.page.size)
  174 + },
  175 + _moreCondition() {
  176 + this.scheduleClassesPageInfo.moreCondition = !this.scheduleClassesPageInfo.moreCondition
  177 + },
  178 + async _exportScheduleClasses() {
  179 + try {
  180 + const params = {
  181 + ...this.scheduleClassesPageInfo.conditions,
  182 + communityId: this.communityId,
  183 + pagePath: 'reportStaffMonthScheduleClasses'
  184 + }
  185 + await exportData(params)
  186 + this.$message.success(this.$t('common.exportSuccess'))
  187 + } catch (error) {
  188 + console.error('导出失败:', error)
  189 + this.$message.error(this.$t('common.exportFailed'))
  190 + }
  191 + },
  192 + _staffChangeOrg() {
  193 + this.$refs.chooseOrgTree.open()
  194 + },
  195 + handleSwitchOrg(org) {
  196 + this.scheduleClassesPageInfo.conditions.orgId = org.orgId
  197 + this.scheduleClassesPageInfo.conditions.orgName = org.allOrgName
  198 + },
  199 + _computeTableDivWidth() {
  200 + const mainWidth = document.getElementsByTagName('body')[0].clientWidth -
  201 + (document.getElementById('menu-nav').offsetWidth || 0)
  202 + return `${mainWidth - 55}px`
  203 + },
  204 + handleSizeChange(val) {
  205 + this.page.size = val
  206 + this._listStaffScheduleClassess(this.page.current, val)
  207 + },
  208 + handleCurrentChange(val) {
  209 + this.page.current = val
  210 + this._listStaffScheduleClassess(val, this.page.size)
  211 + }
  212 + }
  213 +}
  214 +</script>
  215 +
  216 +<style lang="scss" scoped>
  217 +.schedule-classes-container {
  218 + padding: 20px;
  219 +
  220 + .box-card {
  221 + margin-bottom: 20px;
  222 + }
  223 +
  224 + .hc-table-div {
  225 + overflow-x: auto;
  226 + }
  227 +
  228 + .labeling-strip {
  229 + padding: 8px;
  230 + margin: 2px;
  231 + border: 1px solid #ebeef5;
  232 + }
  233 +}
  234 +</style>
0 235 \ No newline at end of file
... ...
src/views/system/communitySettingManageLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + communitySettingManage: {
  4 + inputPlaceholder: 'Required, please enter {name}',
  5 + settingTips: 'Reminder: No spaces before or after input',
  6 + saveSuccess: 'Save successfully'
  7 + }
  8 + },
  9 + zh: {
  10 + communitySettingManage: {
  11 + inputPlaceholder: '必填,请输入{name}',
  12 + settingTips: '温馨提示:填写前后不要有空格',
  13 + saveSuccess: '保存成功'
  14 + }
  15 + }
  16 +}
0 17 \ No newline at end of file
... ...
src/views/system/communitySettingManageList.vue 0 → 100644
  1 +<template>
  2 + <div class="community-setting-manage-container">
  3 + <el-row :gutter="20">
  4 + <el-col :span="4">
  5 + <el-card class="setting-type-card">
  6 + <div class="setting-type-list">
  7 + <div v-for="(item, index) in settingTypes" :key="index" class="setting-type-item"
  8 + :class="{ 'active': conditions.settingType === item.statusCd }" @click="handleSwitchSettingType(item)">
  9 + {{ item.name }}
  10 + </div>
  11 + </div>
  12 + </el-card>
  13 + </el-col>
  14 + <el-col :span="20">
  15 + <el-card>
  16 + <div slot="header" class="flex justify-between">
  17 + <span>{{ currentSettingName }}</span>
  18 + </div>
  19 + <div class="setting-content text-left">
  20 + <el-form label-position="right" label-width="120px">
  21 + <el-form-item v-for="(item, index) in settingKeys" :key="index" :label="item.settingName">
  22 + <el-input v-model="item.settingValue"
  23 + :placeholder="$t('communitySettingManage.inputPlaceholder', { name: item.settingName })" />
  24 + <div class="setting-remark">{{ item.remark }}</div>
  25 + </el-form-item>
  26 + </el-form>
  27 + <div class="setting-tips">{{ $t('communitySettingManage.settingTips') }}</div>
  28 + <div class="text-right">
  29 + <el-button type="primary" @click="handleSaveSettings">
  30 + <i class="el-icon-check"></i>
  31 + {{ $t('common.submit') }}
  32 + </el-button>
  33 + </div>
  34 + </div>
  35 + </el-card>
  36 + </el-col>
  37 + </el-row>
  38 + </div>
  39 +</template>
  40 +
  41 +<script>
  42 +import { getCommunityId } from '@/api/community/communityApi'
  43 +import { getDict } from '@/api/community/communityApi'
  44 +import {
  45 + listCommunitySettingKey,
  46 + saveCommunitySetting
  47 +} from '@/api/system/communitySettingManageApi'
  48 +
  49 +export default {
  50 + name: 'CommunitySettingManageList',
  51 + data() {
  52 + return {
  53 + communityId: '',
  54 + settingTypes: [],
  55 + settingKeys: [],
  56 + currentSettingName: '',
  57 + conditions: {
  58 + settingType: '',
  59 + communityId: ''
  60 + }
  61 + }
  62 + },
  63 + created() {
  64 + this.communityId = getCommunityId()
  65 + this.conditions.communityId = this.communityId
  66 + this.initData()
  67 + },
  68 + methods: {
  69 + async initData() {
  70 + try {
  71 + const data = await getDict('community_setting_key', 'setting_type')
  72 + this.settingTypes = data
  73 + if (data && data.length > 0) {
  74 + this.handleSwitchSettingType(data[0])
  75 + }
  76 + } catch (error) {
  77 + console.error('获取字典数据失败:', error)
  78 + }
  79 + },
  80 + async handleSwitchSettingType(item) {
  81 + this.conditions.settingType = item.statusCd
  82 + this.currentSettingName = item.name
  83 + await this.loadSettingKeys()
  84 + },
  85 + async loadSettingKeys() {
  86 + try {
  87 + const params = {
  88 + communityId: this.communityId,
  89 + settingType: this.conditions.settingType
  90 + }
  91 + const { data } = await listCommunitySettingKey(params)
  92 + this.settingKeys = data.map(item => {
  93 + return {
  94 + ...item,
  95 + settingValue: item.settingValue || ''
  96 + }
  97 + })
  98 + } catch (error) {
  99 + console.error('获取设置项失败:', error)
  100 + }
  101 + },
  102 + async handleSaveSettings() {
  103 + try {
  104 + const params = {
  105 + communityId: this.communityId,
  106 + keys: this.settingKeys,
  107 + settingType: this.conditions.settingType
  108 + }
  109 + await saveCommunitySetting(params)
  110 + this.$message.success(this.$t('common.saveSuccess'))
  111 + this.loadSettingKeys()
  112 + } catch (error) {
  113 + console.error('保存设置失败:', error)
  114 + }
  115 + }
  116 + }
  117 +}
  118 +</script>
  119 +
  120 +<style lang="scss" scoped>
  121 +.community-setting-manage-container {
  122 + padding: 20px;
  123 +
  124 + .setting-type-card {
  125 + height: 100%;
  126 +
  127 + .setting-type-list {
  128 + .setting-type-item {
  129 + padding: 10px;
  130 + margin-bottom: 5px;
  131 + cursor: pointer;
  132 + border-radius: 4px;
  133 + text-align: center;
  134 +
  135 + &:hover {
  136 + background-color: #f5f7fa;
  137 + }
  138 +
  139 + &.active {
  140 + background-color: #409eff;
  141 + color: white;
  142 + }
  143 + }
  144 + }
  145 + }
  146 +
  147 + .setting-content {
  148 + padding: 20px;
  149 +
  150 + .setting-remark {
  151 + margin-top: 5px;
  152 + font-size: 12px;
  153 + color: #909399;
  154 + }
  155 +
  156 + .setting-tips {
  157 + margin: 20px 0;
  158 + font-size: 12px;
  159 + color: #f56c6c;
  160 + }
  161 + }
  162 +}
  163 +</style>
0 164 \ No newline at end of file
... ...
src/views/system/storeInfoManageLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + storeInfoManage: {
  4 + basicInfo: 'Basic Information',
  5 + storeName: 'Store Name',
  6 + storeAddress: 'Store Address',
  7 + contactNumber: 'Contact Number',
  8 + storeType: 'Store Type',
  9 + legalPerson: 'Legal Person',
  10 + nearbyLandmark: 'Nearby Landmark',
  11 + operation: 'Operation',
  12 + edit: 'Edit',
  13 + storeInfo: 'Store Information',
  14 + attributeName: 'Attribute Name',
  15 + attributeValue: 'Attribute Value',
  16 + fetchError: 'Failed to fetch store information'
  17 + },
  18 + editStoreInfo: {
  19 + title: 'Edit Store Information',
  20 + storeName: 'Store Name',
  21 + storeNamePlaceholder: 'Store name cannot be modified',
  22 + storeAddress: 'Store Address',
  23 + storeAddressPlaceholder: 'Required, please enter store address',
  24 + contactNumber: 'Contact Number',
  25 + contactNumberPlaceholder: 'Required, please enter contact number',
  26 + nearbyLandmark: 'Nearby Landmark',
  27 + nearbyLandmarkPlaceholder: 'Required, please enter nearby landmark',
  28 + mapX: 'Map X Coordinate',
  29 + mapXPlaceholder: 'Optional, please enter X coordinate',
  30 + mapY: 'Map Y Coordinate',
  31 + mapYPlaceholder: 'Optional, please enter Y coordinate',
  32 + validate: {
  33 + nameRequired: 'Store name is required',
  34 + nameMaxLength: 'Store name cannot exceed 100 characters',
  35 + addressRequired: 'Store address is required',
  36 + addressMaxLength: 'Store address cannot exceed 200 characters',
  37 + telRequired: 'Contact number is required',
  38 + telMaxLength: 'Contact number cannot exceed 11 digits',
  39 + landmarkRequired: 'Nearby landmark is required',
  40 + landmarkMaxLength: 'Nearby landmark cannot exceed 200 characters'
  41 + }
  42 + },
  43 + editStoreAttr: {
  44 + title: 'Edit Store Attribute',
  45 + attributePlaceholder: 'Store attribute is required',
  46 + validate: {
  47 + valueRequired: 'Attribute value is required',
  48 + valueMaxLength: 'Attribute value cannot exceed 50 characters',
  49 + attrIdRequired: 'Attribute ID cannot be empty'
  50 + }
  51 + }
  52 + },
  53 + zh: {
  54 + storeInfoManage: {
  55 + basicInfo: '基本信息',
  56 + storeName: '商户名称',
  57 + storeAddress: '商户地址',
  58 + contactNumber: '联系电话',
  59 + storeType: '商户类型',
  60 + legalPerson: '企业法人',
  61 + nearbyLandmark: '附近地标',
  62 + operation: '操作',
  63 + edit: '修改',
  64 + storeInfo: '商户信息',
  65 + attributeName: '属性名称',
  66 + attributeValue: '属性值',
  67 + fetchError: '获取商户信息失败'
  68 + },
  69 + editStoreInfo: {
  70 + title: '修改商户信息',
  71 + storeName: '商户名称',
  72 + storeNamePlaceholder: '商户名称不能修改',
  73 + storeAddress: '商户地址',
  74 + storeAddressPlaceholder: '必填,请填写商户地址',
  75 + contactNumber: '电话',
  76 + contactNumberPlaceholder: '必填,请填写联系电话',
  77 + nearbyLandmark: '附近地标',
  78 + nearbyLandmarkPlaceholder: '必填,请填写附近地标',
  79 + mapX: '地区X坐标',
  80 + mapXPlaceholder: '选填,请填写x坐标',
  81 + mapY: '地区Y坐标',
  82 + mapYPlaceholder: '选填,请填写y坐标',
  83 + validate: {
  84 + nameRequired: '商户名称必填',
  85 + nameMaxLength: '商户名称不能超过100位',
  86 + addressRequired: '商户地址必填',
  87 + addressMaxLength: '商户地址不能超过200位',
  88 + telRequired: '联系电话必填',
  89 + telMaxLength: '联系电话最多11位',
  90 + landmarkRequired: '附件地标必填',
  91 + landmarkMaxLength: '附件地标位置最多200位'
  92 + }
  93 + },
  94 + editStoreAttr: {
  95 + title: '修改商户属性',
  96 + attributePlaceholder: '商户属性必填',
  97 + validate: {
  98 + valueRequired: '必填',
  99 + valueMaxLength: '属性值不能超过50位',
  100 + attrIdRequired: '属性id不能为空'
  101 + }
  102 + }
  103 + }
  104 +}
0 105 \ No newline at end of file
... ...
src/views/system/storeInfoManageList.vue 0 → 100644
  1 +<template>
  2 + <div class="store-info-manage-container animated fadeInRight">
  3 + <el-row :gutter="20">
  4 + <el-col :span="24">
  5 + <el-card class="box-card">
  6 + <div slot="header" class="flex justify-between">
  7 + <span>{{ $t('storeInfoManage.basicInfo') }}</span>
  8 + </div>
  9 + <el-table :data="listStoreManageInfo.listStores" border style="width: 100%" v-loading="loading">
  10 + <el-table-column prop="name" :label="$t('storeInfoManage.storeName')" align="center" />
  11 + <el-table-column prop="address" :label="$t('storeInfoManage.storeAddress')" align="center" />
  12 + <el-table-column prop="tel" :label="$t('storeInfoManage.contactNumber')" align="center" />
  13 + <el-table-column prop="storeTypeName" :label="$t('storeInfoManage.storeType')" align="center" />
  14 + <el-table-column prop="artificialPerson" :label="$t('storeInfoManage.legalPerson')" align="center" />
  15 + <el-table-column prop="nearByLandmarks" :label="$t('storeInfoManage.nearbyLandmark')" align="center" />
  16 + <el-table-column :label="$t('storeInfoManage.operation')" align="center" width="150">
  17 + <template slot-scope="scope">
  18 + <el-button size="mini" type="primary" @click="_openEditStoreInfoModel(scope.row)">
  19 + {{ $t('storeInfoManage.edit') }}
  20 + </el-button>
  21 + </template>
  22 + </el-table-column>
  23 + </el-table>
  24 + <el-pagination :current-page.sync="page.current" :page-sizes="[10, 20, 30, 50]" :page-size="page.size"
  25 + :total="page.total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange"
  26 + @current-change="handleCurrentChange" />
  27 + </el-card>
  28 + </el-col>
  29 + </el-row>
  30 +
  31 + <el-row :gutter="20" style="margin-top: 20px">
  32 + <el-col :span="24">
  33 + <el-card class="box-card">
  34 + <div slot="header" class="flex justify-between">
  35 + <span>{{ $t('storeInfoManage.storeInfo') }}</span>
  36 + </div>
  37 + <el-table :data="listStoreManageInfo.storeAttrDtoList" border style="width: 100%">
  38 + <el-table-column prop="name" :label="$t('storeInfoManage.attributeName')" align="center" />
  39 + <el-table-column prop="value" :label="$t('storeInfoManage.attributeValue')" align="center" />
  40 + <el-table-column :label="$t('storeInfoManage.operation')" align="center" width="150">
  41 + <template slot-scope="scope">
  42 + <el-button size="mini" type="primary" @click="_openEditStoreAttrModel(scope.row)">
  43 + {{ $t('storeInfoManage.edit') }}
  44 + </el-button>
  45 + </template>
  46 + </el-table-column>
  47 + </el-table>
  48 + </el-card>
  49 + </el-col>
  50 + </el-row>
  51 +
  52 + <edit-store-info ref="editStoreInfo" @success="handleSuccess" />
  53 + <edit-store-attr ref="editStoreAttr" @success="handleSuccess" />
  54 + </div>
  55 +</template>
  56 +
  57 +<script>
  58 +import { listStores } from '@/api/system/storeInfoManageApi'
  59 +import EditStoreInfo from '@/components/system/editStoreInfo'
  60 +import EditStoreAttr from '@/components/system/editStoreAttr'
  61 +
  62 +export default {
  63 + name: 'StoreInfoManageList',
  64 + components: {
  65 + EditStoreInfo,
  66 + EditStoreAttr
  67 + },
  68 + data() {
  69 + return {
  70 + loading: false,
  71 + listStoreManageInfo: {
  72 + listStores: [],
  73 + storeAttrDtoList: [],
  74 + conditions: {
  75 + name: '',
  76 + storeTypeCd: '',
  77 + tel: ''
  78 + }
  79 + },
  80 + page: {
  81 + current: 1,
  82 + size: 10,
  83 + total: 0
  84 + }
  85 + }
  86 + },
  87 + created() {
  88 + this._listListStores()
  89 + },
  90 + methods: {
  91 + async _listListStores() {
  92 + try {
  93 + this.loading = true
  94 + const params = {
  95 + page: this.page.current,
  96 + row: this.page.size,
  97 + ...this.listStoreManageInfo.conditions
  98 + }
  99 + const { data, total } = await listStores(params)
  100 + this.listStoreManageInfo.listStores = data
  101 + this.listStoreManageInfo.storeAttrDtoList = data[0].storeAttrDtoList || []
  102 + this.page.total = total
  103 + } catch (error) {
  104 + this.$message.error(this.$t('storeInfoManage.fetchError'))
  105 + } finally {
  106 + this.loading = false
  107 + }
  108 + },
  109 + _openEditStoreInfoModel(storeInfo) {
  110 + this.$refs.editStoreInfo.open(storeInfo)
  111 + },
  112 + _openEditStoreAttrModel(storeAttr) {
  113 + this.$refs.editStoreAttr.open(storeAttr)
  114 + },
  115 + handleSuccess() {
  116 + this._listListStores()
  117 + },
  118 + handleSizeChange(val) {
  119 + this.page.size = val
  120 + this._listListStores()
  121 + },
  122 + handleCurrentChange(val) {
  123 + this.page.current = val
  124 + this._listListStores()
  125 + }
  126 + }
  127 +}
  128 +</script>
  129 +
  130 +<style lang="scss" scoped>
  131 +.store-info-manage-container {
  132 + padding: 20px;
  133 +
  134 + .box-card {
  135 + margin-bottom: 20px;
  136 + }
  137 +
  138 + .el-pagination {
  139 + margin-top: 15px;
  140 + text-align: right;
  141 + }
  142 +}
  143 +</style>
0 144 \ No newline at end of file
... ...
src/views/system/workflowManageLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + workflowManage: {
  4 + search: {
  5 + title: 'Search Conditions',
  6 + flowId: 'Please enter workflow code',
  7 + flowName: 'Please enter workflow name',
  8 + flowType: 'Please select workflow type'
  9 + },
  10 + list: {
  11 + title: 'Workflow Information'
  12 + },
  13 + table: {
  14 + flowId: 'Workflow Code',
  15 + flowName: 'Workflow Name',
  16 + flowType: 'Workflow Type',
  17 + createTime: 'Create Time'
  18 + },
  19 + operation: {
  20 + setting: 'Setting',
  21 + viewImage: 'Flow Chart'
  22 + },
  23 + fetchError: 'Failed to fetch workflow data',
  24 + imageError: 'Failed to load workflow image'
  25 + }
  26 + },
  27 + zh: {
  28 + workflowManage: {
  29 + search: {
  30 + title: '查询条件',
  31 + flowId: '请输入流程编码',
  32 + flowName: '请输入流程名称',
  33 + flowType: '请选择流程类型'
  34 + },
  35 + list: {
  36 + title: '流程信息'
  37 + },
  38 + table: {
  39 + flowId: '流程编码',
  40 + flowName: '流程名称',
  41 + flowType: '流程类型',
  42 + createTime: '创建时间'
  43 + },
  44 + operation: {
  45 + setting: '设置流程',
  46 + viewImage: '流程图'
  47 + },
  48 + fetchError: '获取流程数据失败',
  49 + imageError: '加载流程图失败'
  50 + }
  51 + }
  52 +}
0 53 \ No newline at end of file
... ...
src/views/system/workflowManageList.vue 0 → 100644
  1 +<template>
  2 + <div class="workflow-manage-container animated fadeInRight">
  3 + <el-row>
  4 + <el-col :span="24">
  5 + <el-card class="box-card">
  6 + <div slot="header" class="flex justify-between">
  7 + <span>{{ $t('workflowManage.search.title') }}</span>
  8 + </div>
  9 + <div class="search-wrapper">
  10 + <el-row :gutter="20">
  11 + <el-col :span="6">
  12 + <el-input v-model="searchForm.flowId" :placeholder="$t('workflowManage.search.flowId')" clearable
  13 + @keyup.enter.native="handleSearch" />
  14 + </el-col>
  15 + <el-col :span="8">
  16 + <el-select v-model="searchForm.flowType" :placeholder="$t('workflowManage.search.flowType')"
  17 + style="width:100%" clearable>
  18 + <el-option v-for="item in flowTypes" :key="item.statusCd" :label="item.name" :value="item.statusCd" />
  19 + </el-select>
  20 + </el-col>
  21 + <el-col :span="6">
  22 + <el-input v-model="searchForm.flowName" :placeholder="$t('workflowManage.search.flowName')" clearable
  23 + @keyup.enter.native="handleSearch" />
  24 + </el-col>
  25 + <el-col :span="4">
  26 + <el-button type="primary" @click="handleSearch">
  27 + <i class="el-icon-search"></i>
  28 + {{ $t('common.search') }}
  29 + </el-button>
  30 + <el-button @click="handleReset">
  31 + <i class="el-icon-refresh"></i>
  32 + {{ $t('common.reset') }}
  33 + </el-button>
  34 + </el-col>
  35 + </el-row>
  36 + </div>
  37 + </el-card>
  38 + </el-col>
  39 + </el-row>
  40 +
  41 + <el-row>
  42 + <el-col :span="24">
  43 + <el-card class="box-card">
  44 + <div slot="header" class="flex justify-between">
  45 + <span>{{ $t('workflowManage.list.title') }}</span>
  46 + </div>
  47 + <el-table v-loading="loading" :data="tableData" border style="width: 100%">
  48 + <el-table-column prop="flowId" :label="$t('workflowManage.table.flowId')" align="center" />
  49 + <el-table-column prop="flowName" :label="$t('workflowManage.table.flowName')" align="center" />
  50 + <el-table-column prop="flowTypeName" :label="$t('workflowManage.table.flowType')" align="center" />
  51 + <el-table-column prop="createTime" :label="$t('workflowManage.table.createTime')" align="center" />
  52 + <el-table-column :label="$t('common.operation')" align="center" width="200">
  53 + <template slot-scope="scope">
  54 + <el-button size="mini" type="primary" @click="handleEdit(scope.row)">
  55 + {{ $t('workflowManage.operation.setting') }}
  56 + </el-button>
  57 + <el-button size="mini" type="success" @click="handleViewImage(scope.row)">
  58 + {{ $t('workflowManage.operation.viewImage') }}
  59 + </el-button>
  60 + </template>
  61 + </el-table-column>
  62 + </el-table>
  63 +
  64 + <el-pagination :current-page.sync="page.current" :page-sizes="[10, 20, 30, 50]" :page-size="page.size"
  65 + :total="page.total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange"
  66 + @current-change="handleCurrentChange" />
  67 + </el-card>
  68 + </el-col>
  69 + </el-row>
  70 +
  71 + <view-image ref="viewImage" />
  72 + </div>
  73 +</template>
  74 +
  75 +<script>
  76 +import { listWorkflows, listWorkflowImage } from '@/api/system/workflowManageApi'
  77 +import { getDict } from '@/api/community/communityApi'
  78 +import { getCommunityId } from '@/api/community/communityApi'
  79 +import ViewImage from '@/components/system/viewImage'
  80 +
  81 +export default {
  82 + name: 'WorkflowManageList',
  83 + components: {
  84 + ViewImage
  85 + },
  86 + data() {
  87 + return {
  88 + loading: false,
  89 + searchForm: {
  90 + flowId: '',
  91 + flowName: '',
  92 + flowType: ''
  93 + },
  94 + tableData: [],
  95 + flowTypes: [],
  96 + page: {
  97 + current: 1,
  98 + size: 10,
  99 + total: 0
  100 + },
  101 + communityId: ''
  102 + }
  103 + },
  104 + created() {
  105 + this.communityId = getCommunityId()
  106 + this.getList()
  107 + this.getDictData()
  108 + },
  109 + methods: {
  110 + async getList() {
  111 + try {
  112 + this.loading = true
  113 + const params = {
  114 + page: this.page.current,
  115 + row: this.page.size,
  116 + communityId: this.communityId,
  117 + ...this.searchForm
  118 + }
  119 + const { data, total } = await listWorkflows(params)
  120 + this.tableData = data
  121 + this.page.total = total
  122 + } catch (error) {
  123 + this.$message.error(this.$t('workflowManage.fetchError'))
  124 + } finally {
  125 + this.loading = false
  126 + }
  127 + },
  128 + async getDictData() {
  129 + try {
  130 + const data = await getDict('workflow', 'flow_type')
  131 + this.flowTypes = data
  132 + } catch (error) {
  133 + console.error('获取字典数据失败:', error)
  134 + }
  135 + },
  136 + handleSearch() {
  137 + this.page.current = 1
  138 + this.getList()
  139 + },
  140 + handleReset() {
  141 + this.searchForm = {
  142 + flowId: '',
  143 + flowName: '',
  144 + flowType: ''
  145 + }
  146 + this.getList()
  147 + },
  148 + handleEdit(row) {
  149 + this.$router.push({
  150 + path: '/pages/property/workflowSettingManage',
  151 + query: row
  152 + })
  153 + },
  154 + async handleViewImage(row) {
  155 + try {
  156 + const params = {
  157 + communityId: this.communityId,
  158 + flowId: row.flowId
  159 + }
  160 + const { data } = await listWorkflowImage(params)
  161 + this.$refs.viewImage.open({
  162 + url: 'data:image/png;base64,' + data
  163 + })
  164 + } catch (error) {
  165 + this.$message.error(this.$t('workflowManage.imageError'))
  166 + }
  167 + },
  168 + handleSizeChange(val) {
  169 + this.page.size = val
  170 + this.getList()
  171 + },
  172 + handleCurrentChange(val) {
  173 + this.page.current = val
  174 + this.getList()
  175 + }
  176 + }
  177 +}
  178 +</script>
  179 +
  180 +<style lang="scss" scoped>
  181 +.workflow-manage-container {
  182 + padding: 0;
  183 + margin: 0;
  184 +
  185 + .box-card {
  186 + margin-bottom: 20px;
  187 + }
  188 +
  189 + .search-wrapper {
  190 + .el-col {
  191 + margin-bottom: 10px;
  192 + }
  193 + }
  194 +
  195 + .el-pagination {
  196 + margin-top: 20px;
  197 + text-align: right;
  198 + }
  199 +}
  200 +</style>
0 201 \ No newline at end of file
... ...
src/views/system/workflowSettingManageLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + workflowSettingManage: {
  4 + title: 'Process Settings',
  5 + flowName: 'Process Name',
  6 + flowNamePlaceholder: 'Required, please enter the title',
  7 + description: 'Process Description',
  8 + descriptionPlaceholder: 'Optional, please enter the process description',
  9 + submitterEnd: 'Submitter End',
  10 + submitterEndPlaceholder: 'Required, please select whether the submitter needs to confirm',
  11 + yes: 'Yes',
  12 + no: 'No',
  13 + flowSteps: 'Process Steps',
  14 + addStep: 'Add Step',
  15 + step: 'Step',
  16 + selectStaff: 'Select Staff',
  17 + normalFlow: 'Normal Process',
  18 + countersign: 'Countersign',
  19 + deleteStep: 'Delete Step',
  20 + addStaff: 'Add Staff',
  21 + deleteStaff: 'Delete Staff',
  22 + instructions: 'Instructions',
  23 + instruction1: 'At least two steps are required, the last step must be a fixed warehouse manager',
  24 + instruction2: 'Warehouse managers cannot participate in the review process',
  25 + instruction3: 'Warehouse managers cannot be dynamically assigned',
  26 + instruction4: 'Must configure the corresponding process',
  27 + invalidOperation: 'Invalid operation'
  28 + },
  29 + selectStaff: {
  30 + title: 'Select Staff',
  31 + orgInfo: 'Organization Information',
  32 + staffInfo: 'Staff Information',
  33 + submitter: 'Submitter',
  34 + dynamicAssign: 'Dynamic Assign',
  35 + fetchStaffError: 'Failed to fetch staff information'
  36 + },
  37 + orgTree: {
  38 + fetchError: 'Failed to fetch organization tree'
  39 + }
  40 + },
  41 + zh: {
  42 + workflowSettingManage: {
  43 + title: '流程设置',
  44 + flowName: '流程名称',
  45 + flowNamePlaceholder: '必填,请填写标题',
  46 + description: '流程说明',
  47 + descriptionPlaceholder: '选填,请填写流程说明',
  48 + submitterEnd: '提交者结束',
  49 + submitterEndPlaceholder: '必填,请选择最后环节是否提交者确认',
  50 + yes: '是',
  51 + no: '否',
  52 + flowSteps: '流程步骤',
  53 + addStep: '添加步骤',
  54 + step: '第',
  55 + selectStaff: '选择员工',
  56 + normalFlow: '普通流程',
  57 + countersign: '会签',
  58 + deleteStep: '删除步骤',
  59 + addStaff: '添加员工',
  60 + deleteStaff: '删除员工',
  61 + instructions: '说明',
  62 + instruction1: '添加步骤必须要两步以上,最后一步为固定仓库管理员',
  63 + instruction2: '仓库管理不能参与审核流程',
  64 + instruction3: '仓库管理员不能为动态指定人员',
  65 + instruction4: '必须配置对应的流程',
  66 + invalidOperation: '操作错误'
  67 + },
  68 + selectStaff: {
  69 + title: '选择员工',
  70 + orgInfo: '组织信息',
  71 + staffInfo: '员工信息',
  72 + submitter: '提交者',
  73 + dynamicAssign: '动态指定',
  74 + fetchStaffError: '获取员工信息失败'
  75 + },
  76 + orgTree: {
  77 + fetchError: '获取组织树失败'
  78 + }
  79 + }
  80 +}
0 81 \ No newline at end of file
... ...
src/views/system/workflowSettingManageList.vue 0 → 100644
  1 +<template>
  2 + <div class="workflow-setting-container">
  3 + <el-row :gutter="20">
  4 + <el-col :span="24">
  5 + <el-card>
  6 + <div slot="header" class="clearfix">
  7 + <span>{{ $t('workflowSettingManage.title') }}</span>
  8 + </div>
  9 + <el-form label-width="120px">
  10 + <el-form-item :label="$t('workflowSettingManage.flowName')">
  11 + <el-input v-model="workflowSettingInfo.flowName"
  12 + :placeholder="$t('workflowSettingManage.flowNamePlaceholder')" disabled />
  13 + </el-form-item>
  14 +
  15 + <el-form-item :label="$t('workflowSettingManage.description')">
  16 + <el-input v-model="workflowSettingInfo.describle" type="textarea"
  17 + :placeholder="$t('workflowSettingManage.descriptionPlaceholder')" :rows="3" />
  18 + </el-form-item>
  19 +
  20 + <el-form-item :label="$t('workflowSettingManage.submitterEnd')">
  21 + <el-select v-model="workflowSettingInfo.startNodeFinish" style="width:100%"
  22 + :placeholder="$t('workflowSettingManage.submitterEndPlaceholder')">
  23 + <el-option :label="$t('workflowSettingManage.yes')" value="Y" />
  24 + <el-option :label="$t('workflowSettingManage.no')" value="N" />
  25 + </el-select>
  26 + </el-form-item>
  27 +
  28 + <el-form-item :label="$t('workflowSettingManage.flowSteps')">
  29 + <div class="step-actions">
  30 + <el-button type="primary" @click="addWorkflowStep">
  31 + {{ $t('workflowSettingManage.addStep') }}
  32 + </el-button>
  33 + </div>
  34 +
  35 + <div v-for="(item, index) in workflowSettingInfo.steps" :key="index" class="step-item">
  36 + <div class="step-header">
  37 + <span>{{ $t('workflowSettingManage.step') }} {{ index + 1 }}</span>
  38 +
  39 + <el-button type="text" @click="chooseStaff(item)">
  40 + {{ item.staffId ? item.staffName : $t('workflowSettingManage.selectStaff') }}
  41 + </el-button>
  42 +
  43 + <el-radio-group v-model="item.type" @change="chooseType(item)">
  44 + <el-radio :label="2">{{ $t('workflowSettingManage.normalFlow') }}</el-radio>
  45 + <el-radio v-if="index !== 0" :label="1">
  46 + {{ $t('workflowSettingManage.countersign') }}
  47 + </el-radio>
  48 + </el-radio-group>
  49 +
  50 + <el-button type="text" @click="deleteStep(item)">
  51 + {{ $t('workflowSettingManage.deleteStep') }}
  52 + </el-button>
  53 +
  54 + <el-button v-if="item.type == 1" type="text" @click="addStaff(item)">
  55 + {{ $t('workflowSettingManage.addStaff') }}
  56 + </el-button>
  57 + </div>
  58 +
  59 + <div v-for="(subItem, subIndex) in item.subStaff" :key="subIndex" class="sub-staff-item">
  60 + <el-button type="text" @click="chooseStaff(subItem)">
  61 + {{ subItem.staffId ? subItem.staffName : $t('workflowSettingManage.selectStaff') }}
  62 + </el-button>
  63 +
  64 + <el-button type="text" @click="deleteStaff(item, subItem)">
  65 + {{ $t('workflowSettingManage.deleteStaff') }}
  66 + </el-button>
  67 + </div>
  68 + </div>
  69 + </el-form-item>
  70 + </el-form>
  71 +
  72 + <div class="form-actions">
  73 + <el-button type="primary" @click="saveWorkflowSettingInfo">
  74 + {{ $t('common.submit') }}
  75 + </el-button>
  76 + <el-button type="warning" @click="_goBack">
  77 + {{ $t('common.back') }}
  78 + </el-button>
  79 + </div>
  80 + </el-card>
  81 + </el-col>
  82 + </el-row>
  83 +
  84 + <el-row :gutter="20" class="mt-20">
  85 + <el-col :span="24">
  86 + <el-card>
  87 + <div slot="header" class="clearfix">
  88 + <span>{{ $t('workflowSettingManage.instructions') }}</span>
  89 + </div>
  90 + <div v-if="workflowSettingInfo.flowType === '70007'">
  91 + <p>1. {{ $t('workflowSettingManage.instruction1') }}</p>
  92 + <p>2. {{ $t('workflowSettingManage.instruction2') }}</p>
  93 + <p>3. {{ $t('workflowSettingManage.instruction3') }}</p>
  94 + <p>4. {{ $t('workflowSettingManage.instruction4') }}</p>
  95 + </div>
  96 + <!-- 其他流程类型的说明类似 -->
  97 + </el-card>
  98 + </el-col>
  99 + </el-row>
  100 +
  101 + <select-staff ref="selectStaff" @selectStaffs="handleStaffSelected"/>
  102 + </div>
  103 +</template>
  104 +
  105 +<script>
  106 +import { getCommunityId } from '@/api/community/communityApi'
  107 +import SelectStaff from '@/components/staff/SelectStaff'
  108 +import {
  109 + getWorkflowSteps,
  110 + updateWorkflow
  111 +} from '@/api/system/workflowSettingManageApi'
  112 +
  113 +export default {
  114 + name: 'WorkflowSettingManageList',
  115 + components: {
  116 + SelectStaff
  117 + },
  118 + data() {
  119 + return {
  120 + communityId: '',
  121 + workflowSettingInfo: {
  122 + flowId: '',
  123 + flowName: '',
  124 + flowType: '',
  125 + describle: '',
  126 + startNodeFinish: 'Y',
  127 + steps: []
  128 + }
  129 + }
  130 + },
  131 + created() {
  132 + this.communityId = getCommunityId()
  133 + this._initWorkflowSettingInfo()
  134 + },
  135 + methods: {
  136 + async _initWorkflowSettingInfo() {
  137 + const flowId = this.$route.query.flowId
  138 +
  139 + if (!flowId) {
  140 + this.$message.error(this.$t('workflowSettingManage.invalidOperation'))
  141 + this.$router.go(-1)
  142 + return
  143 + }
  144 +
  145 + this.workflowSettingInfo.flowId = flowId
  146 + this.workflowSettingInfo.flowName = this.$route.query.flowName
  147 + this.workflowSettingInfo.flowType = this.$route.query.flowType
  148 + this.workflowSettingInfo.startNodeFinish = this.$route.query.startNodeFinish || 'Y'
  149 +
  150 + try {
  151 + const { data } = await getWorkflowSteps({
  152 + communityId: this.communityId,
  153 + flowId
  154 + })
  155 + this._freshResStep(data)
  156 + } catch (error) {
  157 + this.$message.error(error.message)
  158 + }
  159 + },
  160 +
  161 + _freshResStep(data) {
  162 + // 处理步骤数据的逻辑
  163 + console.log(data)
  164 + },
  165 +
  166 + addWorkflowStep() {
  167 + const step = {
  168 + seq: this.workflowSettingInfo.steps.length,
  169 + staffId: '',
  170 + staffName: '',
  171 + type: '2',
  172 + subStaff: []
  173 + }
  174 + this.workflowSettingInfo.steps.push(step)
  175 + },
  176 +
  177 + chooseStaff(item) {
  178 + item.from = this._getStaffFromType()
  179 + this.$refs.selectStaff.open(item)
  180 + },
  181 +
  182 + _getStaffFromType() {
  183 + // 根据flowType返回对应的from值
  184 + },
  185 +
  186 + _goBack() {
  187 + this.$router.go(-1)
  188 + },
  189 +
  190 + deleteStep(step) {
  191 + this.workflowSettingInfo.steps = this.workflowSettingInfo.steps.filter(
  192 + s => s.seq !== step.seq
  193 + )
  194 + },
  195 +
  196 + addStaff(step) {
  197 + step.subStaff.push({
  198 + id: this.$uuid(),
  199 + staffId: '',
  200 + staffName: '',
  201 + staffRole: '1001'
  202 + })
  203 + },
  204 +
  205 + deleteStaff(step, subStaff) {
  206 + step.subStaff = step.subStaff.filter(s => s.id !== subStaff.id)
  207 + },
  208 +
  209 + chooseType(item) {
  210 + if (item.type === '1') {
  211 + item.subStaff = []
  212 + }
  213 + },
  214 +
  215 + handleStaffSelected(staffs) {
  216 + console.log(staffs)
  217 + },
  218 +
  219 + async saveWorkflowSettingInfo() {
  220 + try {
  221 + await updateWorkflow({
  222 + ...this.workflowSettingInfo,
  223 + communityId: this.communityId
  224 + })
  225 + this.$message.success(this.$t('common.saveSuccess'))
  226 + this._goBack()
  227 + } catch (error) {
  228 + this.$message.error(error.message)
  229 + }
  230 + }
  231 + }
  232 +}
  233 +</script>
  234 +
  235 +<style lang="scss" scoped>
  236 +.workflow-setting-container {
  237 + padding: 20px;
  238 +
  239 + .step-item {
  240 + margin-bottom: 20px;
  241 + padding: 15px;
  242 + border: 1px solid #ebeef5;
  243 + border-radius: 4px;
  244 +
  245 + .step-header {
  246 + display: flex;
  247 + align-items: center;
  248 + margin-bottom: 10px;
  249 +
  250 + >* {
  251 + margin-right: 15px;
  252 + }
  253 + }
  254 +
  255 + .sub-staff-item {
  256 + margin-left: 40px;
  257 + margin-bottom: 10px;
  258 +
  259 + >* {
  260 + margin-right: 15px;
  261 + }
  262 + }
  263 + }
  264 +
  265 + .form-actions {
  266 + text-align: right;
  267 + margin-top: 20px;
  268 + }
  269 +
  270 + .mt-20 {
  271 + margin-top: 20px;
  272 + }
  273 +}
  274 +</style>
0 275 \ No newline at end of file
... ...