index.vue 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. <template>
  2. <div class="main animate__animated animate__backInRight" v-loading="loading">
  3. <custom-search-bar :fields="fields.filter((f) => f.isSearch)" v-model="searchForm" @search="search" @reset="toReset">
  4. <template #type>
  5. <el-option v-for="i in typeList" :key="i._id" :label="i.label" :value="i.value"></el-option>
  6. </template>
  7. </custom-search-bar>
  8. <custom-button-bar :fields="buttonFields" @add="toAdd"></custom-button-bar>
  9. <custom-table :data="data" :fields="fields" @query="search" :total="total" :opera="opera" @sign="toSign" @exam="toExam" @edit="toEdit" @delete="toDelete">
  10. <template #is_use="{ row }">
  11. <el-tag v-if="row.is_use == '0'" type="success" @click="toUse(row, '1')">启用</el-tag>
  12. <el-tag v-else type="info" @click="toUse(row, '0')">禁用</el-tag>
  13. </template>
  14. </custom-table>
  15. <el-dialog v-model="dialog.show" :title="dialog.title" :destroy-on-close="false" @close="toClose">
  16. <el-row>
  17. <el-col :span="24" v-if="dialog.type == '1'">
  18. <custom-form v-model="form" :fields="formFields" :rules="rules" @save="toSave">
  19. <template #is_use>
  20. <el-radio v-for="i in isUseList" :key="i._id" :label="i.value">{{ i.label }}</el-radio>
  21. </template>
  22. <template #type>
  23. <el-option v-for="i in typeList" :key="i._id" :label="i.label" :value="i.value"></el-option>
  24. </template>
  25. <template #rules>
  26. <div class="rules">
  27. <custom-form v-model="form.rules" :fields="rulesFields" :useSave="false">
  28. <template #rules11>
  29. <WangEditor v-model="form.rules.rules11" />
  30. </template>
  31. </custom-form>
  32. </div>
  33. </template>
  34. <template #brief>
  35. <WangEditor v-model="form.brief" />
  36. </template>
  37. </custom-form>
  38. </el-col>
  39. <el-col :span="24" v-if="dialog.type == '2'">
  40. <custom-form v-model="examForm" :fields="examFormFields" :rules="examRules" @save="toExamSave">
  41. <template #status>
  42. <el-option v-for="i in statusList" :key="i._id" :label="i.label" :value="i.value"></el-option>
  43. </template>
  44. </custom-form>
  45. </el-col>
  46. </el-row>
  47. </el-dialog>
  48. </div>
  49. </template>
  50. <script setup>
  51. // API 引用
  52. import { cloneDeep, get } from 'lodash-es'
  53. const $checkRes = inject('$checkRes')
  54. const { t } = useI18n()
  55. // 路由
  56. const router = useRouter()
  57. // 接口
  58. import { MatchStore } from '@/store/api/platform/match'
  59. import { DictDataStore } from '@/store/api/system/dictData'
  60. const store = MatchStore()
  61. const dictDataStore = DictDataStore()
  62. const data = ref([])
  63. const searchForm = ref({})
  64. const fields = [
  65. { label: t('pages.match.name'), model: 'name', isSearch: true },
  66. { label: t('pages.match.type'), model: 'type', isSearch: true, type: 'select', format: (i) => getDict(i, 'type') },
  67. { label: t('pages.match.money'), model: 'money' },
  68. { label: t('pages.match.is_use'), model: 'is_use', custom: true, format: (i) => getDict(i, 'is_use') },
  69. { label: t('pages.match.match_status'), model: 'match_status', format: (i) => getDict(i, 'match') },
  70. { label: t('pages.match.status'), model: 'status', format: (i) => getDict(i, 'status') }
  71. ]
  72. const opera = [
  73. { label: t('common.sign'), method: 'sign' },
  74. { label: t('common.update'), method: 'edit', display: (i) => i.is_use === '1' },
  75. { label: t('common.exam'), method: 'exam', type: 'warning', display: (i) => i.status === '0' },
  76. { label: t('common.delete'), method: 'delete', confirm: true, type: 'danger', display: (i) => i.is_use === '1' }
  77. ]
  78. const buttonFields = [{ label: t('common.add'), method: 'add' }]
  79. let skip = 0
  80. let limit = inject('limit')
  81. const total = ref(20)
  82. // 字典表
  83. const isUseList = ref([])
  84. const statusList = ref([])
  85. const typeList = ref([])
  86. const matchList = ref([])
  87. // 加载中
  88. const loading = ref(false)
  89. const formFields = ref([
  90. { label: t('pages.match.name'), model: 'name' },
  91. { label: t('pages.match.type'), model: 'type', type: 'select' },
  92. { label: t('pages.match.money'), model: 'money' },
  93. { label: t('pages.match.time'), model: 'time', type: 'daterange' },
  94. { label: t('pages.match.is_use'), model: 'is_use', type: 'radio' },
  95. { label: t('pages.match.rules'), model: 'rules', custom: true },
  96. { label: t('pages.match.brief'), model: 'brief', custom: true }
  97. ])
  98. const rules = reactive({ name: [{ required: true, message: t('pages.match.titleMessage'), trigger: 'blur' }] })
  99. const dialog = ref({ type: '1', show: false, title: t('pages.match.addDialogTitle') })
  100. const form = ref({ rules: {} })
  101. // 审核
  102. const examFormFields = [{ label: t('pages.match.status'), model: 'status', type: 'select' }]
  103. const examRules = reactive({ title: [{ required: true, message: t('pages.match.titleMessage'), trigger: 'blur' }] })
  104. const examForm = ref({})
  105. // 赛事规则
  106. const rulesFields = ref([
  107. { label: t('pages.match.rules1'), model: 'rules1', type: 'textarea' },
  108. { label: t('pages.match.rules2'), model: 'rules2', type: 'textarea' },
  109. { label: t('pages.match.rules3'), model: 'rules3', type: 'textarea' },
  110. { label: t('pages.match.rules4'), model: 'rules4', type: 'textarea' },
  111. { label: t('pages.match.rules5'), model: 'rules5', type: 'textarea' },
  112. { label: t('pages.match.rules6'), model: 'rules6', type: 'textarea' },
  113. { label: t('pages.match.rules7'), model: 'rules7', type: 'textarea' },
  114. { label: t('pages.match.rules8'), model: 'rules8', type: 'textarea' },
  115. { label: t('pages.match.rules9'), model: 'rules9', type: 'textarea' },
  116. { label: t('pages.match.rules10'), model: 'rules10', type: 'textarea' },
  117. { label: t('pages.match.rules11'), model: 'rules11', custom: true }
  118. ])
  119. // 请求
  120. onMounted(async () => {
  121. loading.value = true
  122. await searchOther()
  123. await search({ skip, limit })
  124. loading.value = false
  125. })
  126. const searchOther = async () => {
  127. let result
  128. // 是否使用
  129. result = await dictDataStore.query({ code: 'isUse', is_use: '0' })
  130. if ($checkRes(result)) isUseList.value = result.data
  131. // 类型
  132. result = await dictDataStore.query({ code: 'matchType', is_use: '0' })
  133. if ($checkRes(result)) typeList.value = result.data
  134. // 状态
  135. result = await dictDataStore.query({ code: 'examStatus', is_use: '0' })
  136. if ($checkRes(result)) statusList.value = result.data
  137. // 赛事状态
  138. result = await dictDataStore.query({ code: 'matchStatus', is_use: '0' })
  139. if ($checkRes(result)) matchList.value = result.data
  140. }
  141. const search = async (query = { skip: 0, limit }) => {
  142. const info = { skip: query.skip, limit: query.limit, ...searchForm.value }
  143. const res = await store.query(info)
  144. if (res.errcode == '0') {
  145. data.value = res.data
  146. total.value = res.total
  147. }
  148. }
  149. // 字典数据转换
  150. const getDict = (data, model) => {
  151. let res
  152. if (model == 'is_use') res = isUseList.value.find((f) => f.value == data)
  153. else if (model == 'status') res = statusList.value.find((f) => f.value == data)
  154. else if (model == 'type') res = typeList.value.find((f) => f.value == data)
  155. else if (model == 'match') res = matchList.value.find((f) => f.value == data)
  156. return get(res, 'label')
  157. }
  158. // 添加
  159. const toAdd = () => {
  160. dialog.value = { type: '1', show: true, title: t('pages.match.addDialogTitle') }
  161. }
  162. // 修改
  163. const toEdit = (data) => {
  164. form.value = data
  165. dialog.value = { type: '1', show: true, title: t('pages.match.upDialogTitle') }
  166. }
  167. // 报名管理
  168. const toSign = (data) => {
  169. router.push({ path: '/match/sign', query: { id: data._id } })
  170. }
  171. // 删除
  172. const toDelete = async (data) => {
  173. const res = await store.del(data._id)
  174. if ($checkRes(res, true)) {
  175. search({ skip: 0, limit })
  176. }
  177. }
  178. const toSave = async () => {
  179. const data = cloneDeep(form.value)
  180. const other = { status: '0' }
  181. let res
  182. if (get(data, '_id')) res = await store.update({ ...data, ...other })
  183. else res = await store.create({ ...data, ...other })
  184. if ($checkRes(res, true)) {
  185. search({ skip: 0, limit })
  186. toClose()
  187. }
  188. }
  189. // 审核
  190. const toExam = (data) => {
  191. examForm.value = data
  192. dialog.value = { type: '2', show: true, title: t('pages.match.examDialogTitle') }
  193. }
  194. // 审核保存
  195. const toExamSave = async () => {
  196. const data = cloneDeep(examForm.value)
  197. let res = await store.update(data)
  198. if ($checkRes(res, true)) {
  199. search({ skip: 0, limit })
  200. toClose()
  201. }
  202. }
  203. // 开启或禁用
  204. const toUse = async (data, is_use) => {
  205. let res = await store.update({ _id: get(data, '_id'), is_use })
  206. if ($checkRes(res, true)) {
  207. search({ skip: 0, limit })
  208. }
  209. }
  210. // 重置
  211. const toReset = async () => {
  212. searchForm.value = {}
  213. await search({ skip, limit })
  214. }
  215. const toClose = () => {
  216. form.value = { rules: {} }
  217. dialog.value = { show: false }
  218. }
  219. </script>
  220. <style scoped lang="scss">
  221. .rules {
  222. width: 100%;
  223. border: 1px solid #dcdfe6;
  224. padding: 10px;
  225. border-radius: 5px;
  226. :deep(.el-form-item) {
  227. margin-bottom: 18px !important;
  228. }
  229. }
  230. </style>