Просмотр исходного кода

Merge branch 'main' of http://git.cc-lotus.info/Information/cxyy-admin

lrf 8 месяцев назад
Родитель
Сommit
67e2e58c98

+ 2 - 1
src/lang/package/zh-cn/menus.js

@@ -9,7 +9,8 @@ export default {
   password: '修改密码',
   log: '日志管理',
   message: '站内消息',
-  journal: '行研产研'
+  journal: '行研产研',
+  journal_notes: '期刊管理'
 
   // system_menus: '菜单设置',
   // system_userMenus: '用户目录',

+ 4 - 0
src/lang/package/zh-cn/pages.js

@@ -207,6 +207,8 @@ export default {
   },
   match: {
     addDialogTitle: '新增需赛事',
+    signDialogTitle: '报名管理',
+    scoreDialogTitle: '分数管理',
     upDialogTitle: '修改赛事',
     examDialogTitle: '审核赛事',
     name: '赛事名称',
@@ -226,6 +228,7 @@ export default {
     brief: '常见问题',
     status: '审核状态',
     match_status: '赛事状态',
+    process: '赛事流程',
     is_use: '是否启用',
     is_show: '是否在首页显示',
     order_num: '显示顺序',
@@ -355,6 +358,7 @@ export default {
     file: '封面',
     name: '名称',
     brief: '简介',
+    content: '内容',
     status: '审核状态',
     is_use: '是否启用',
     titleMessage: '请输入期刊名称'

+ 1 - 1
src/layout/parts/Sidebar.vue

@@ -77,7 +77,7 @@ watch(
     padding: 5px 0;
 
     .logo-image {
-      width: 135px;
+      width: 160px;
       height: 40px;
     }
 

+ 1 - 1
src/router/guard.js

@@ -3,7 +3,7 @@ import NProgress from 'nprogress'
 import 'nprogress/nprogress.css'
 import { UserStore } from '@/store/user'
 import { ElMessage } from 'element-plus'
-const whiteList = ['/redirect', '/login', '/401', '/404', '/test']
+const whiteList = ['/redirect', '/login', '/401', '/404', '/test', '/journal/notes']
 NProgress.configure({ showSpinner: false }) // 进度条
 const dontRedirectList = ['/login', '/', '/401', '/404']
 const getRedirectUri = (route) => {

+ 11 - 0
src/router/modules/journal.js

@@ -10,5 +10,16 @@ export const routes = [
       alwaysShow: false
     },
     component: () => import('@/views/journal/index.vue')
+  },
+  {
+    path: '/journal/notes',
+    name: 'journal_notes',
+    meta: {
+      title: i18n.global.t('menus.journal_notes'),
+      affix: true,
+      keepAlive: true,
+      alwaysShow: false
+    },
+    component: () => import('@/views/journal/notes.vue')
   }
 ]

+ 5 - 6
src/utils/axios-wrapper.js

@@ -1,17 +1,17 @@
 /* eslint-disable no-console */
 /* eslint-disable no-param-reassign */
 
-import { get, isObject, pick, isArray } from 'lodash-es'
+import { get, pick, isArray } from 'lodash-es'
 import Axios from 'axios'
-import { trimData, isNullOrUndefined } from './util-methods'
+import { isNullOrUndefined } from './util-methods'
 import { ErrorCode } from './error-code'
 import router from '@/router'
 import i18n from '@/lang'
 import * as crypto from './crypto'
-import { ElMessage, ElMessageBox } from 'element-plus'
+import { ElMessageBox } from 'element-plus'
 let currentRequests = 0
 const { VITE_APP_BASE_API, VITE_USE_CRYPTO } = import.meta.env
-const userErrorCodeList = ['NOT_LOGIN', 'ACCOUNT_HAS_EXPIRED', 'ACCOUNT_LOGGED_IN_ELESWHERE', 'USER_NOT_FOUND', 'USER_IS_DISABLED', 'ROLE_IS_DISABLED', 'SERVICE_REPEAT', 'SERVICE_COPY', 'SERVICE_END']
+const userErrorCodeList = ['NOT_LOGIN', 'ACCOUNT_HAS_EXPIRED', 'ACCOUNT_LOGGED_IN_ELESWHERE', 'USER_NOT_FOUND', 'USER_IS_DISABLED', 'ROLE_IS_DISABLED']
 export class AxiosWrapper {
   constructor({ baseUrl = VITE_APP_BASE_API, unwrap = true } = {}) {
     this.baseUrl = baseUrl
@@ -126,6 +126,7 @@ export class AxiosWrapper {
         console.warn(`[${uri}] fail: ${errcode}-${errmsg} ${details}`)
         if (errcode !== 0) {
           if (userErrorCodeList.includes(errcode)) {
+            console.log(errcode)
             const nowRouteFullPath = router.currentRoute.value.fullPath
             ElMessageBox.confirm(errmsg, i18n.global.t('common.user_confirm'), {
               confirmButtonText: i18n.global.t('common.re_login'),
@@ -138,8 +139,6 @@ export class AxiosWrapper {
               .catch(() => {
                 if (nowRouteFullPath !== '/login') location.reload()
               })
-          } else {
-            ElMessage.error(errmsg)
           }
         }
         return returnRes

+ 2 - 0
src/utils/error-code.js

@@ -17,6 +17,7 @@ const DATABASE_FAULT = 'DATABASE_FAULT'
 const FILE_FAULT = 'FILE_FAULT'
 const USER_NOT_BIND = 'USER_NOT_BIND'
 const BUSINESS = 'BUSINESS'
+const SERVICE_COPY = 'SERVICE_COPY'
 
 const ErrorCode = {
   [UNKNOW]: -1,
@@ -35,6 +36,7 @@ const ErrorCode = {
   [DATABASE_FAULT]: -14,
   [FILE_FAULT]: -15,
   [USER_NOT_BIND]: -16,
+  [SERVICE_COPY]: -17,
   [BUSINESS]: -100
 }
 

+ 209 - 0
src/views/information/parts/match/score/index.vue

@@ -0,0 +1,209 @@
+<template>
+  <div id="index">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight" v-loading="loading">
+        <el-row justify="center">
+          <el-col :span="16">
+            <el-steps style="max-width: 600px" :active="active" finish-status="success">
+              <el-step v-for="(item, index) in matchPathList" :key="index" :title="item.name" />
+            </el-steps>
+          </el-col>
+        </el-row>
+        <el-col :span="24" class="one">
+          <custom-button-bar :fields="buttonFields" @add="toAdd"></custom-button-bar>
+        </el-col>
+        <el-col :span="24" class="two">
+          <div class="table">
+            <el-table :data="data" border>
+              <el-table-column type="index" label="序号" width="80" align="center" />
+              <el-table-column prop="matchPath_name" label="流程" align="center" />
+              <el-table-column prop="sign_name" label="选手" align="center" />
+              <el-table-column prop="score" label="分数" align="center">
+                <template #default="scope">
+                  <el-input v-model="scope.row.score" type="number" placeholder="请输入分数" />
+                </template>
+              </el-table-column>
+              <el-table-column label="操作" align="center" width="100">
+                <template #default="scope">
+                  <el-link :underline="false" type="primary" size="mini" @click="onSave(scope.row)" style="margin-right: 10px">保存</el-link>
+                  <el-link :underline="false" type="danger" size="mini" @click="onDelete(scope.row)"> 删除 </el-link>
+                </template>
+              </el-table-column>
+            </el-table>
+            <el-col :span="24" class="thr">
+              <el-pagination background layout="prev, pager, next" :total="total" :page-size="limit" v-model:current-page="currentPage" @current-change="changePage" @size-change="sizeChange" />
+            </el-col>
+          </div>
+          <div class="button">
+            <el-button @click="back">上一步</el-button>
+            <el-button @click="next">下一步</el-button>
+          </div>
+        </el-col>
+      </el-col>
+    </el-row>
+    <el-dialog v-model="dialog.show" :title="dialog.title" :destroy-on-close="false" @close="toClose">
+      <el-row>
+        <el-col :span="24" v-if="dialog.type == '1'">
+          <custom-form v-model="form" :fields="formFields" :rules="rules" @save="toSave">
+            <template #matchPath>
+              <el-option v-for="i in matchPathList" disabled :key="i.id" :label="i.name" :value="i.id"></el-option>
+            </template>
+            <template #sign>
+              <el-option v-for="i in signList" :key="i.id" :label="i.name" :value="i.id">
+                <span style="float: left">{{ i.name }}</span>
+                <span style="float: right; font-size: 13px" v-if="i.card">身份证号码:{{ i.card }} </span>
+              </el-option>
+            </template>
+          </custom-form>
+        </el-col>
+      </el-row>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import moment from 'moment'
+import { cloneDeep, get } from 'lodash-es'
+const $checkRes = inject('$checkRes')
+const { t } = useI18n()
+// 接口
+import { ScoreStore } from '@/store/api/platform/score'
+import { SignStore } from '@/store/api/platform/sign'
+import { ProcessStore } from '@/store/api/platform/process'
+const store = ScoreStore()
+const processStore = ProcessStore()
+const signStore = SignStore()
+const props = defineProps({
+  matchInfo: { type: Object }
+})
+const match = computed({
+  get() {
+    return props.matchInfo
+  }
+})
+const id = ref('')
+const data = ref([])
+const searchForm = ref({})
+let skip = 0
+let limit = inject('limit')
+const total = ref(0)
+const currentPage = ref(1)
+const formFields = ref([
+  { label: t('pages.score.matchPath'), model: 'matchPath', type: 'select' },
+  { label: t('pages.score.sign'), model: 'sign', type: 'select' },
+  { label: t('pages.score.score'), model: 'score', type: 'number' }
+])
+const rules = reactive({
+  sign: [{ required: true, message: t('pages.score.signMessage'), trigger: 'blur' }],
+  score: [{ required: true, message: t('pages.score.scoreMessage'), trigger: 'blur' }]
+})
+const dialog = ref({ type: '1', show: false, title: t('pages.score.DialogTitle') })
+const buttonFields = [{ label: t('common.create'), method: 'add' }]
+const form = ref({})
+// 字典表
+const matchPathList = ref([])
+const signList = ref([])
+// 加载中
+const loading = ref(false)
+const searchOther = async () => {
+  let result
+  // 流程
+  result = await processStore.query({ match: id.value, is_use: '0' })
+  if ($checkRes(result)) matchPathList.value = result.data
+  // 选手
+  result = await signStore.query({ match: id.value, status: '1' })
+  if ($checkRes(result)) signList.value = result.data
+}
+const search = async (query = { skip, limit }) => {
+  skip = query.skip
+  limit = query.limit
+  const info = { skip: query.skip, limit: query.limit, ...searchForm.value, match: id.value, matchPath: matchPathList.value[active.value].id }
+  const res = await store.list(info)
+  if (res.errcode == '0') {
+    data.value = res.data
+    total.value = res.total
+  }
+}
+// 添加
+const toAdd = () => {
+  form.value = { matchPath: matchPathList.value[active.value].id }
+  dialog.value = { type: '1', show: true, title: t('pages.score.addDialogTitle') }
+}
+// 删除
+const onDelete = async (data) => {
+  const res = await store.del(data.id)
+  if ($checkRes(res, true)) {
+    search({ skip, limit })
+  }
+}
+const onSave = async (data) => {
+  let res
+  const other = { time: moment().format('YYYY-MM-DD'), match: id.value }
+  if (get(data, 'id')) res = await store.update({ ...data, ...other })
+  else res = await store.create({ ...data, ...other })
+  if ($checkRes(res, true)) {
+    search({ skip, limit })
+  }
+}
+const toSave = async () => {
+  const data = cloneDeep(form.value)
+  const other = { time: moment().format('YYYY-MM-DD'), match: id.value }
+  let res
+  if (get(data, 'id')) res = await store.update({ ...data, ...other })
+  else res = await store.create({ ...data, ...other })
+  if ($checkRes(res, true)) {
+    search({ skip, limit })
+    toClose()
+  }
+}
+const toClose = () => {
+  form.value = {}
+  dialog.value = { show: false }
+}
+const active = ref(0)
+// 上一步
+const back = async () => {
+  if (active.value > 0) active.value = active.value - 1
+  else active.value = 0
+  await search({ skip, limit })
+}
+// 下一步
+const next = async () => {
+  if (active.value < matchPathList.value.length - 1) active.value = active.value + 1
+  else active.value = 0
+  await search({ skip, limit })
+}
+// 分页
+const changePage = (page = currentPage.value) => {
+  search({ skip: (page - 1) * limit, limit: limit })
+}
+const sizeChange = (limits) => {
+  limit = limits
+  currentPage.value = 1
+  search({ skip: 0, limit: limit })
+}
+watch(
+  match,
+  async (item) => {
+    id.value = item.id
+    loading.value = true
+    await searchOther()
+    await search({ skip, limit })
+    loading.value = false
+  },
+  {
+    immediate: true //初始化立即执行
+  }
+)
+</script>
+<style scoped lang="scss">
+.button {
+  text-align: center;
+  margin: 12px 0 0 0;
+}
+.thr {
+  display: flex;
+  justify-content: center;
+  margin: 20px 0 0 0;
+}
+</style>

+ 160 - 0
src/views/information/parts/match/sign/index.vue

@@ -0,0 +1,160 @@
+<template>
+  <div id="index">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight" v-loading="loading">
+        <el-col :span="24" class="one">
+          <custom-search-bar :fields="fields.filter((f) => f.isSearch)" v-model="searchForm" @search="search" @reset="toReset"></custom-search-bar>
+          <custom-table :data="data" :fields="fields" @query="search" :total="total" :opera="opera" @view="toView" @exam="toExam">
+            <template #is_use="{ row }">
+              <el-tag v-if="row.is_use == '0'" type="success" @click="toUse(row, '1')">启用</el-tag>
+              <el-tag v-else type="info" @click="toUse(row, '0')">禁用</el-tag>
+            </template>
+          </custom-table>
+        </el-col>
+      </el-col>
+    </el-row>
+    <el-dialog v-model="dialog.show" :title="dialog.title" :destroy-on-close="false" @close="toClose">
+      <el-row>
+        <el-col :span="24" v-if="dialog.type == '1'">
+          <custom-form v-model="form" :fields="formFields" :useSave="false">
+            <template #cardType>
+              <el-option v-for="i in cardTypeList" :key="i.id" :label="i.label" :value="i.value"></el-option>
+            </template>
+          </custom-form>
+        </el-col>
+        <el-col :span="24" v-if="dialog.type == '2'">
+          <custom-form v-model="examForm" :fields="examFormFields" :rules="examRules" @save="toExamSave">
+            <template #status>
+              <el-option v-for="i in statusList" :key="i.id" :label="i.label" :value="i.value"></el-option>
+            </template>
+          </custom-form>
+        </el-col>
+      </el-row>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { cloneDeep, get } from 'lodash-es'
+const $checkRes = inject('$checkRes')
+const { t } = useI18n()
+// 接口
+import { SignStore } from '@/store/api/platform/sign'
+import { DictDataStore } from '@/store/api/system/dictData'
+const store = SignStore()
+const dictDataStore = DictDataStore()
+
+const data = ref([])
+const searchForm = ref({})
+const fields = [
+  { label: t('pages.sign.name'), model: 'name', isSearch: true },
+  { label: t('pages.sign.phone'), model: 'phone', isSearch: true },
+  { label: t('pages.sign.card'), model: 'card', isSearch: true },
+  { label: t('pages.sign.communication'), model: 'communication' },
+  { label: t('pages.sign.email'), model: 'email' },
+  { label: t('pages.sign.time'), model: 'time' },
+  { label: t('pages.match.status'), model: 'status', format: (i) => getDict(i, 'status') }
+]
+const opera = [
+  { label: t('common.view'), method: 'view' },
+  { label: t('common.exam'), method: 'exam', type: 'warning', display: (i) => i.status === '0' }
+]
+let skip = 0
+let limit = inject('limit')
+const total = ref(0)
+const formFields = ref([
+  { label: t('pages.sign.name'), model: 'name' },
+  { label: t('pages.sign.phone'), model: 'phone' },
+  { label: t('pages.sign.cardType'), model: 'cardType', type: 'select' },
+  { label: t('pages.sign.card'), model: 'card' },
+  { label: t('pages.sign.communication'), model: 'communication' },
+  { label: t('pages.sign.email'), model: 'email' },
+  { label: t('pages.sign.remark'), model: 'remark', type: 'textarea' }
+])
+const dialog = ref({ type: '1', show: false, title: t('pages.sign.DialogTitle') })
+const form = ref({})
+// 审核
+const examFormFields = [{ label: t('pages.match.status'), model: 'status', type: 'select' }]
+const examRules = reactive({
+  status: [{ required: true, message: t('common.statusMessage'), trigger: 'blur' }]
+})
+const examForm = ref({})
+// 字典表
+const cardTypeList = ref([])
+const statusList = ref([])
+// 加载中
+const loading = ref(false)
+const searchOther = async () => {
+  let result
+  // 证件类型
+  result = await dictDataStore.query({ code: 'cardType', is_use: '0' })
+  if ($checkRes(result)) cardTypeList.value = result.data
+  // 状态
+  result = await dictDataStore.query({ code: 'examStatus', is_use: '0' })
+  if ($checkRes(result)) statusList.value = result.data
+}
+const props = defineProps({
+  matchInfo: { type: Object }
+})
+const match = computed({
+  get() {
+    return props.matchInfo
+  }
+})
+const id = ref('')
+const search = async (query = { skip, limit }) => {
+  skip = query.skip
+  limit = query.limit
+  const info = { skip: query.skip, limit: query.limit, ...searchForm.value, match: id.value }
+  const res = await store.query(info)
+  if (res.errcode == '0') {
+    data.value = res.data
+    total.value = res.total
+  }
+}
+// 字典数据转换
+const getDict = (data, model) => {
+  if (data) {
+    let res
+    if (model == 'status') res = statusList.value.find((f) => f.value == data)
+    return get(res, 'label')
+  }
+}
+// 查看
+const toView = async (data) => {
+  form.value = data
+  dialog.value = { type: '1', show: true, title: t('pages.user.dialogTitle') }
+}
+// 审核
+const toExam = (data) => {
+  examForm.value = data
+  dialog.value = { type: '2', show: true, title: t('pages.match.examDialogTitle') }
+}
+// 审核保存
+const toExamSave = async () => {
+  const data = cloneDeep(examForm.value)
+  let res = await store.update({ id: data.id, status: data.status })
+  if ($checkRes(res, true)) {
+    search({ skip, limit })
+    toClose()
+  }
+}
+const toClose = () => {
+  examForm.value = {}
+  dialog.value = { show: false }
+}
+watch(
+  match,
+  async (item) => {
+    id.value = item.id
+    loading.value = true
+    await searchOther()
+    await search({ skip, limit })
+    loading.value = false
+  },
+  {
+    immediate: true //初始化立即执行
+  }
+)
+</script>
+<style scoped lang="scss"></style>

+ 91 - 11
src/views/information/parts/platform/match.vue

@@ -99,6 +99,40 @@
             <template #brief>
               <WangEditor v-model="form.brief" />
             </template>
+            <template #process>
+              <el-col :span="24" class="tables">
+                <el-col :span="24" class="tables_1">
+                  <el-button type="primary" @click="addProcess()">添加</el-button>
+                </el-col>
+                <el-col :span="24">
+                  <el-table :data="processList" border>
+                    <el-table-column type="index" label="序号" width="80" align="center"> </el-table-column>
+                    <el-table-column prop="name" label="流程名称" align="center">
+                      <template #default="scope">
+                        <el-input v-model="scope.row.name" placeholder="请输入流程名称" />
+                      </template>
+                    </el-table-column>
+                    <el-table-column prop="order_num" label="排序" align="center">
+                      <template #default="scope">
+                        <el-input v-model="scope.row.order_num" type="number" placeholder="请输入排序" />
+                      </template>
+                    </el-table-column>
+                    <el-table-column prop="is_use" label="是否启用" align="center">
+                      <template #default="scope">
+                        <el-select v-model="scope.row.is_use" placeholder="请选择是否启用">
+                          <el-option v-for="item in isUseList" :key="item.value" :label="item.label" :value="item.value" />
+                        </el-select>
+                      </template>
+                    </el-table-column>
+                    <el-table-column label="操作" align="center" width="100">
+                      <template #default="scope">
+                        <el-button type="danger" @click="delProcess(scope.row)">删除</el-button>
+                      </template>
+                    </el-table-column>
+                  </el-table>
+                </el-col>
+              </el-col>
+            </template>
           </custom-form>
         </el-col>
         <el-col :span="24" v-if="dialog.type == '2'">
@@ -109,7 +143,10 @@
           </custom-form>
         </el-col>
         <el-col :span="24" v-if="dialog.type === '3'">
-          报名
+          <sign :matchInfo="matchInfo"></sign>
+        </el-col>
+        <el-col :span="24" v-if="dialog.type === '4'">
+          <score :matchInfo="matchInfo"></score>
         </el-col>
       </el-row>
     </el-dialog>
@@ -117,14 +154,17 @@
 </template>
 
 <script setup>
+import moment from 'moment'
 // API 引用
 import { cloneDeep, get } from 'lodash-es'
 const $checkRes = inject('$checkRes')
 const { t } = useI18n()
-// 路由
-const router = useRouter()
+// 组件
+import sign from '../match/sign/index.vue'
+import score from '../match/score/index.vue'
 // 接口
 import { MatchStore } from '@/store/api/platform/match'
+import { ProcessStore } from '@/store/api/platform/process'
 import { DictDataStore } from '@/store/api/system/dictData'
 import { TagsStore } from '@/store/api/system/tags'
 import { SectorStore } from '@/store/api/system/sector'
@@ -132,6 +172,7 @@ const store = MatchStore()
 const dictDataStore = DictDataStore()
 const tagsStore = TagsStore()
 const sectorStore = SectorStore()
+const processStore = ProcessStore()
 const data = ref([])
 const searchForm = ref({})
 const fields = [
@@ -146,7 +187,6 @@ const fields = [
 ]
 const opera = [
   { label: t('common.score'), method: 'score', display: (i) => i.match_status == '2' },
-  { label: t('common.process'), method: 'process', display: (i) => i.scale === '0' },
   { label: t('common.sign'), method: 'sign', type: 'warning' },
   {
     label: t('common.update'),
@@ -168,6 +208,7 @@ const buttonFields = [
 let skip = 0
 let limit = inject('limit')
 const total = ref(0)
+const matchInfo = ref({})
 // 字典表
 const isUseList = ref([])
 const statusList = ref([])
@@ -178,6 +219,8 @@ const tagsList = ref([])
 const sectorList = ref([])
 const matchTypeList = ref([])
 const scaleList = ref([])
+// 流程列表
+const processList = ref([])
 // 多选列表
 const selectList = ref([])
 // 加载中
@@ -201,6 +244,7 @@ const formFields = ref([
   { label: t('pages.match.rules'), model: 'rules', custom: true },
   { label: t('pages.match.brief'), model: 'brief', custom: true },
   { label: t('pages.match.video'), model: 'video', custom: true },
+  { label: t('pages.match.process'), model: 'process', custom: true },
   { label: t('pages.match.match_status'), model: 'match_status', type: 'select' }
 ])
 const rules = reactive({
@@ -277,6 +321,11 @@ const search = async (query = { skip, limit }) => {
     total.value = res.total
   }
 }
+const searchProcess = async () => {
+  const info = { match: form.value.id }
+  const res = await processStore.query(info)
+  if (res.errcode == '0') processList.value = res.data
+}
 // 字典数据转换
 const getDict = (data, model) => {
   if (data) {
@@ -313,24 +362,23 @@ const toAdd = () => {
   dialog.value = { type: '1', show: true, title: t('pages.match.addDialogTitle') }
 }
 // 修改
-const toEdit = (data) => {
+const toEdit = async (data) => {
   data.time = [data.start_time, data.end_time]
   if (!data.file) data.file = []
   if (!data.video) data.video = []
   form.value = data
+  await searchProcess()
   dialog.value = { type: '1', show: true, title: t('pages.match.upDialogTitle') }
 }
 // 报名管理
 const toSign = (data) => {
-  router.push({ path: '/match/sign', query: { id: data.id } })
-}
-// 流程管理
-const toProcess = (data) => {
-  router.push({ path: '/match/process', query: { id: data.id } })
+  matchInfo.value = data
+  dialog.value = { type: '3', show: true, title: t('pages.match.signDialogTitle') }
 }
 // 分数管理
 const toScore = (data) => {
-  router.push({ path: '/match/score', query: { id: data.id } })
+  matchInfo.value = data
+  dialog.value = { type: '4', show: true, title: t('pages.match.scoreDialogTitle') }
 }
 // 删除
 const toDelete = async (data) => {
@@ -350,6 +398,7 @@ const toSave = async () => {
   if (get(data, 'id')) res = await store.update({ ...data, ...other })
   else res = await store.create({ ...data, ...other })
   if ($checkRes(res, true)) {
+    toProcessSave()
     search({ skip, limit })
     toClose()
   }
@@ -383,6 +432,32 @@ const toUse = async (data, is_use) => {
     })
     .catch(() => {})
 }
+// 流程添加
+const toProcessSave = async () => {
+  for (const val of processList.value) {
+    const data = cloneDeep(val)
+    delete data.sid
+    if (get(data, 'id')) await processStore.update(data)
+    else await processStore.create(data)
+  }
+}
+// 流程添加
+const addProcess = () => {
+  let list = processList.value || []
+  list.push({ sid: moment().valueOf(), name: '', time: '', is_use: '', match: form.value.id })
+  processList.value = list
+}
+// 流程删除
+const delProcess = async (e) => {
+  if (e.sid) {
+    let list = processList.value.filter((i) => i.sid != e.sid)
+    processList.value = list
+  } else {
+    let list = processList.value.filter((i) => i.id != e.id)
+    processList.value = list
+  }
+  if (e.id) await processStore.del(e.id)
+}
 // 重置
 const toReset = async () => {
   searchForm.value = {}
@@ -411,4 +486,9 @@ const onUpload = (e) => {
     margin-left: 0 !important;
   }
 }
+.tables {
+  .tables_1 {
+    margin: 0 0 10px 0;
+  }
+}
 </style>

+ 0 - 223
src/views/journal/directory.vue

@@ -1,223 +0,0 @@
-<template>
-  <div class="main animate__animated animate__backInRight" v-loading="loading">
-    <el-col :span="24" class="one">
-      <el-button type="primary" @click="toBack()">返回</el-button>
-    </el-col>
-    <custom-search-bar :fields="fields.filter((f) => f.isSearch)" v-model="searchForm" @search="search" @reset="toReset"> </custom-search-bar>
-    <custom-button-bar :fields="buttonFields" @add="toAdd" @select="toMoreDelect"></custom-button-bar>
-    <custom-table :data="data" :fields="fields" @query="search" :total="total" :opera="opera" @exam="toExam" @edit="toEdit" @delete="toDelete" @toSelect="toSelect" :select="true">
-      <template #is_use="{ row }">
-        <el-tag v-if="row.is_use == '0'" type="success" @click="toUse(row, '1')">启用</el-tag>
-        <el-tag v-else type="info" @click="toUse(row, '0')">禁用</el-tag>
-      </template>
-    </custom-table>
-    <el-dialog v-model="dialog.show" :title="dialog.title" :destroy-on-close="false" @close="toClose">
-      <el-row>
-        <el-col :span="24" v-if="dialog.type == '1'">
-          <custom-form v-model="form" :fields="formFields" :rules="rules" @save="toSave">
-            <template #is_use>
-              <el-radio v-for="i in isUseList" :key="i.id" :label="i.value">{{ i.label }}</el-radio>
-            </template>
-            <template #detail>
-              <WangEditor v-model="form.detail" />
-            </template>
-          </custom-form>
-        </el-col>
-        <el-col :span="24" v-if="dialog.type == '2'">
-          <custom-form v-model="examForm" :fields="examFormFields" :rules="examRules" @save="toExamSave">
-            <template #status>
-              <el-option v-for="i in statusList" :key="i.id" :label="i.label" :value="i.value"></el-option>
-            </template>
-          </custom-form>
-        </el-col>
-      </el-row>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup>
-import { cloneDeep, get } from 'lodash-es'
-const $checkRes = inject('$checkRes')
-const { t } = useI18n()
-// 接口
-import { DirectoryStore } from '@/store/api/platform/directory'
-import { DictDataStore } from '@/store/api/system/dictData'
-const store = DirectoryStore()
-const dictDataStore = DictDataStore()
-const data = ref([])
-const searchForm = ref({})
-const fields = [
-  { label: t('pages.directory.name'), model: 'name', isSearch: true },
-  { label: t('pages.directory.client'), model: 'client', isSearch: true },
-  { label: t('pages.directory.partner'), model: 'partner', isSearch: true },
-  { label: t('pages.directory.is_use'), model: 'is_use', custom: true, format: (i) => getDict(i, 'is_use') },
-  { label: t('pages.directory.status'), model: 'status', format: (i) => getDict(i, 'status') }
-]
-const opera = [
-  { label: t('common.update'), method: 'edit' },
-  { label: t('common.exam'), method: 'exam', type: 'warning', display: (i) => i.status === '0' },
-  { label: t('common.delete'), method: 'delete', confirm: true, type: 'danger', display: (i) => i.is_use === '1' }
-]
-const buttonFields = [
-  { label: t('common.create'), method: 'add' },
-  { label: t('common.select'), method: 'select', type: 'danger' }
-]
-let skip = 0
-let limit = inject('limit')
-const total = ref(0)
-// 路由
-const route = useRoute()
-// 字典表
-const isUseList = ref([])
-const statusList = ref([])
-// 多选列表
-const selectList = ref([])
-// 加载中
-const loading = ref(false)
-const formFields = ref([
-  { label: t('pages.directory.file'), model: 'file', custom: true },
-  { label: t('pages.directory.name'), model: 'name' },
-  { label: t('pages.directory.client'), model: 'client' },
-  { label: t('pages.directory.partner'), model: 'partner' },
-  { label: t('pages.directory.is_use'), model: 'is_use', type: 'radio' },
-  { label: t('pages.directory.brief'), model: 'brief', type: 'textarea' },
-  { label: t('pages.directory.detail'), model: 'detail', custom: true }
-])
-const rules = reactive({ name: [{ required: true, message: t('pages.directory.titleMessage'), trigger: 'blur' }] })
-const dialog = ref({ type: '1', show: false, title: t('pages.directory.addDialogTitle') })
-const form = ref({ notes: route.query.id, journal: route.query.journal })
-// 审核
-const examFormFields = [{ label: t('pages.directory.status'), model: 'status', type: 'select' }]
-const examRules = reactive({ status: [{ required: true, message: t('common.statusMessage'), trigger: 'blur' }] })
-const examForm = ref({})
-// 请求
-onMounted(async () => {
-  loading.value = true
-  await searchOther()
-  await search({ skip, limit })
-  loading.value = false
-})
-
-const searchOther = async () => {
-  let result
-  // 是否使用
-  result = await dictDataStore.query({ code: 'isUse', is_use: '0' })
-  if ($checkRes(result)) isUseList.value = result.data
-  // 状态
-  result = await dictDataStore.query({ code: 'examStatus', is_use: '0' })
-  if ($checkRes(result)) statusList.value = result.data
-}
-const search = async (query = { skip, limit }) => {
-  skip = query.skip
-  limit = query.limit
-  const info = { skip: query.skip, limit: query.limit, ...searchForm.value, notes: route.query.id, journal: route.query.journal }
-  const res = await store.query(info)
-  if (res.errcode == '0') {
-    data.value = res.data
-    total.value = res.total
-  }
-}
-// 字典数据转换
-const getDict = (data, model) => {
-  if (data) {
-    let res
-    if (model == 'is_use') res = isUseList.value.find((f) => f.value == data)
-    else if (model == 'status') res = statusList.value.find((f) => f.value == data)
-    return get(res, 'label')
-  }
-}
-// 多选
-const toSelect = (val) => {
-  selectList.value = val
-}
-// 批量删除
-const toMoreDelect = () => {
-  if (selectList.value.length > 0) {
-    ElMessageBox.confirm(`确定批量删除数据?`, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
-      .then(async () => {
-        console.log(selectList.value)
-      })
-      .catch(() => {})
-  } else {
-    ElMessage({
-      message: '未选择要处理的数据!',
-      type: 'warning'
-    })
-  }
-}
-// 添加
-const toAdd = () => {
-  form.value = { notes: route.query.id, journal: route.query.journal }
-  dialog.value = { type: '1', show: true, title: t('pages.directory.addDialogTitle') }
-}
-// 修改
-const toEdit = (data) => {
-  if (!data.file) data.file = []
-  form.value = data
-  dialog.value = { type: '1', show: true, title: t('pages.directory.upDialogTitle') }
-}
-// 删除
-const toDelete = async (data) => {
-  const res = await store.del(data.id)
-  if ($checkRes(res, true)) {
-    search({ skip, limit })
-  }
-}
-const toSave = async () => {
-  const data = cloneDeep(form.value)
-  const other = { status: '0' }
-  let res
-  if (get(data, 'id')) res = await store.update({ ...data, ...other })
-  else res = await store.create({ ...data, ...other })
-  if ($checkRes(res, true)) {
-    search({ skip, limit })
-    toClose()
-  }
-}
-// 审核
-const toExam = (data) => {
-  examForm.value = data
-  dialog.value = { type: '2', show: true, title: t('pages.directory.examDialogTitle') }
-}
-// 审核保存
-const toExamSave = async () => {
-  const data = cloneDeep(examForm.value)
-  let res = await store.update({ id: data.id, status: data.status })
-  if ($checkRes(res, true)) {
-    search({ skip, limit })
-    toClose()
-  }
-}
-// 开启或禁用
-const toUse = async (data, is_use) => {
-  ElMessageBox.confirm(`确定修改【${data.name}】数据?`, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
-    .then(async () => {
-      let res = await store.update({ id: get(data, 'id'), is_use, status: get(data, 'status') })
-      if ($checkRes(res, true)) {
-        search({ skip, limit })
-      }
-    })
-    .catch(() => {})
-}
-// 重置
-const toReset = async () => {
-  searchForm.value = {}
-  await search({ skip, limit })
-}
-const toClose = () => {
-  form.value = { notes: route.query.id, journal: route.query.journal }
-  dialog.value = { show: false }
-}
-// 返回上一页
-const toBack = () => {
-  window.history.go(-1)
-}
-</script>
-<style scoped lang="scss">
-.main {
-  .one {
-    margin: 0 0 10px 0;
-    text-align: right;
-  }
-}
-</style>

+ 5 - 20
src/views/journal/notes.vue

@@ -5,19 +5,7 @@
     </el-col>
     <custom-search-bar :fields="fields.filter((f) => f.isSearch)" v-model="searchForm" @search="search" @reset="toReset"> </custom-search-bar>
     <custom-button-bar :fields="buttonFields" @add="toAdd" @select="toMoreDelect"></custom-button-bar>
-    <custom-table
-      :data="data"
-      :fields="fields"
-      @query="search"
-      :total="total"
-      :opera="opera"
-      @directory="toDirectory"
-      @exam="toExam"
-      @edit="toEdit"
-      @delete="toDelete"
-      @toSelect="toSelect"
-      :select="true"
-    >
+    <custom-table :data="data" :fields="fields" @query="search" :total="total" :opera="opera" @exam="toExam" @edit="toEdit" @delete="toDelete" @toSelect="toSelect" :select="true">
       <template #is_use="{ row }">
         <el-tag v-if="row.is_use == '0'" type="success" @click="toUse(row, '1')">启用</el-tag>
         <el-tag v-else type="info" @click="toUse(row, '0')">禁用</el-tag>
@@ -33,6 +21,9 @@
             <template #is_use>
               <el-radio v-for="i in isUseList" :key="i.id" :label="i.value">{{ i.label }}</el-radio>
             </template>
+            <template #content>
+              <WangEditor v-model="form.content" />
+            </template>
           </custom-form>
         </el-col>
         <el-col :span="24" v-if="dialog.type == '2'">
@@ -51,8 +42,6 @@
 import { cloneDeep, get } from 'lodash-es'
 const $checkRes = inject('$checkRes')
 const { t } = useI18n()
-// 路由
-const router = useRouter()
 // 接口
 import { NotesStore } from '@/store/api/platform/notes'
 import { DictDataStore } from '@/store/api/system/dictData'
@@ -66,7 +55,6 @@ const fields = [
   { label: t('pages.notes.status'), model: 'status', format: (i) => getDict(i, 'status') }
 ]
 const opera = [
-  { label: t('common.directory'), method: 'directory' },
   { label: t('common.update'), method: 'edit' },
   { label: t('common.exam'), method: 'exam', type: 'warning', display: (i) => i.status === '0' },
   { label: t('common.delete'), method: 'delete', confirm: true, type: 'danger', display: (i) => i.is_use === '1' }
@@ -91,6 +79,7 @@ const formFields = ref([
   { label: t('pages.notes.file'), model: 'file', custom: true },
   { label: t('pages.notes.name'), model: 'name' },
   { label: t('pages.notes.is_use'), model: 'is_use', type: 'radio' },
+  { label: t('pages.notes.content'), model: 'content', custom: true },
   { label: t('pages.notes.brief'), model: 'brief', type: 'textarea' }
 ])
 const rules = reactive({ name: [{ required: true, message: t('pages.notes.titleMessage'), trigger: 'blur' }] })
@@ -140,10 +129,6 @@ const getDict = (data, model) => {
 const toSelect = (val) => {
   selectList.value = val
 }
-// 目录管理
-const toDirectory = (data) => {
-  router.push({ path: '/journal/directory', query: { id: data.id, journal: data.journal } })
-}
 // 批量删除
 const toMoreDelect = () => {
   if (selectList.value.length > 0) {