Selaa lähdekoodia

修改中试平台管理

zs 9 kuukautta sitten
vanhempi
commit
018f0487e0

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

@@ -29,6 +29,7 @@ export default {
   match_sign: '报名管理',
   achievement: '成果管理',
   project: '项目管理',
+  footplate: '平台管理',
   log: '日志管理',
   log_opera: '操作日志'
 }

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

@@ -109,6 +109,7 @@ export default {
     title: '标题',
     English: '英文标题',
     href: '路由',
+    partner: '合作伙伴',
     is_use: '是否启用',
     remark: '备注',
     sort: '排序',
@@ -259,6 +260,26 @@ export default {
     is_use: '是否启用',
     titleMessage: '请输入项目名称'
   },
+  footplate: {
+    addDialogTitle: '新增平台',
+    upDialogTitle: '修改平台',
+    examDialogTitle: '审核平台',
+    user: '用户',
+    tags: '标签',
+    industry: '所属产业',
+    name: '名称',
+    build: '建设主体',
+    operate: '运营主体',
+    field: '服务产业领域',
+    area: '所在地区',
+    address: '地址',
+    contacts: '联系人',
+    phone: '联系电话',
+    brief: '简介',
+    status: '审核状态',
+    is_use: '是否启用',
+    titleMessage: '请输入平台名称'
+  },
   sign: {
     name: '姓名',
     phone: '手机号',

+ 40 - 0
src/store/api/platform/footplate.js

@@ -0,0 +1,40 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/footplate'
+const axios = new AxiosWrapper()
+
+export const FootplateStore = defineStore('footplate', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, 'id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    create,
+    update,
+    del
+  }
+})

+ 40 - 0
src/store/api/platform/support.js

@@ -0,0 +1,40 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/support'
+const axios = new AxiosWrapper()
+
+export const SupportStore = defineStore('support', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, 'id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    create,
+    update,
+    del
+  }
+})

+ 2 - 0
src/views/demand/supply/index.vue

@@ -238,6 +238,8 @@ const toDelete = async (data) => {
 const toSave = async () => {
   const data = cloneDeep(form.value)
   const other = { status: '0' }
+  data.start_time = data.time[0]
+  data.end_time = data.time[1]
   let res
   if (get(data, 'id')) res = await store.update({ ...data, ...other })
   else res = await store.create({ ...data, ...other })

+ 246 - 0
src/views/footplate/index.vue

@@ -0,0 +1,246 @@
+<template>
+  <div class="main animate__animated animate__backInRight" v-loading="loading">
+    <custom-search-bar :fields="fields.filter((f) => f.isSearch)" v-model="searchForm" @search="search" @reset="toReset">
+      <template #industry>
+        <el-option v-for="i in sectorList" :key="i.id" :label="i.title" :value="i.title"></el-option>
+      </template>
+    </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 #industry>
+              <el-option v-for="i in sectorList" :key="i.id" :label="i.title" :value="i.title"></el-option>
+            </template>
+            <template #area>
+              <el-cascader v-model="form.area" :props="{ value: 'label', label: 'label' }" :options="cityList" style="width: 100%" />
+            </template>
+            <template #tags>
+              <el-select v-model="form.tags" multiple filterable allow-create default-first-option :reserve-keyword="false" placeholder="请选择标签" style="width: 100%">
+                <el-option v-for="item in tagsList" :key="item.id" :label="item.title" :value="item.title" />
+              </el-select>
+            </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>
+// API 引用
+import { getCity } from '@/utils/city'
+import { cloneDeep, get } from 'lodash-es'
+const $checkRes = inject('$checkRes')
+const { t } = useI18n()
+// 接口
+import { FootplateStore } from '@/store/api/platform/footplate'
+import { DictDataStore } from '@/store/api/system/dictData'
+import { TagsStore } from '@/store/api/system/tags'
+import { SectorStore } from '@/store/api/system/sector'
+const store = FootplateStore()
+const dictDataStore = DictDataStore()
+const tagsStore = TagsStore()
+const sectorStore = SectorStore()
+const data = ref([])
+const searchForm = ref({})
+const fields = [
+  { label: t('pages.footplate.name'), model: 'name', isSearch: true },
+  { label: t('pages.footplate.industry'), model: 'industry', isSearch: true, type: 'select' },
+  { label: t('pages.footplate.tags'), model: 'tags', isSearch: true, format: (i) => getDict(i, 'tags') },
+  { label: t('pages.footplate.build'), model: 'build', isSearch: true },
+  { label: t('pages.footplate.operate'), model: 'operate', isSearch: true },
+  { label: t('pages.footplate.contacts'), model: 'contacts', isSearch: true },
+  { label: t('pages.footplate.phone'), model: 'phone', isSearch: true },
+  { label: t('pages.footplate.is_use'), model: 'is_use', custom: true, format: (i) => getDict(i, 'is_use') },
+  { label: t('pages.footplate.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 isUseList = ref([])
+const statusList = ref([])
+const cityList = ref([])
+const tagsList = ref([])
+const sectorList = ref([])
+// 多选列表
+const selectList = ref([])
+// 加载中
+const loading = ref(false)
+const formFields = ref([
+  { label: t('pages.footplate.name'), model: 'name' },
+  { label: t('pages.footplate.tags'), model: 'tags', custom: true },
+  { label: t('pages.footplate.industry'), model: 'industry', type: 'select' },
+  { label: t('pages.footplate.build'), model: 'build' },
+  { label: t('pages.footplate.operate'), model: 'operate' },
+  { label: t('pages.footplate.field'), model: 'field' },
+  { label: t('pages.footplate.area'), model: 'area', custom: true },
+  { label: t('pages.footplate.address'), model: 'address', type: 'textarea' },
+  { label: t('pages.footplate.contacts'), model: 'contacts' },
+  { label: t('pages.footplate.phone'), model: 'phone' },
+  { label: t('pages.footplate.is_use'), model: 'is_use', type: 'radio' },
+  { label: t('pages.footplate.brief'), model: 'brief', type: 'textarea' }
+])
+const rules = reactive({ name: [{ required: true, message: t('pages.footplate.titleMessage'), trigger: 'blur' }] })
+const dialog = ref({ type: '1', show: false, title: t('pages.footplate.addDialogTitle') })
+const form = ref({})
+// 审核
+const examFormFields = [{ label: t('pages.footplate.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
+  getCity().then((response) => {
+    cityList.value = response.address
+  })
+  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
+  // 标签
+  result = await tagsStore.query({ is_use: '0' })
+  if ($checkRes(result)) tagsList.value = result.data
+  // 行业
+  result = await sectorStore.query({ is_use: '0' })
+  if ($checkRes(result)) sectorList.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 }
+  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)
+    else if (model == 'tags') return data.join(',')
+    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 = { type: '1' }
+  dialog.value = { type: '1', show: true, title: t('pages.footplate.addDialogTitle') }
+}
+// 修改
+const toEdit = (data) => {
+  form.value = data
+  dialog.value = { type: '1', show: true, title: t('pages.footplate.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.footplate.examDialogTitle') }
+}
+// 审核保存
+const toExamSave = async () => {
+  const data = cloneDeep(examForm.value)
+  let res = await store.update(data)
+  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 })
+      if ($checkRes(res, true)) {
+        search({ skip, limit })
+      }
+    })
+    .catch(() => {})
+}
+// 重置
+const toReset = async () => {
+  searchForm.value = {}
+  await search({ skip, limit })
+}
+const toClose = () => {
+  form.value = {}
+  dialog.value = { show: false }
+}
+</script>
+<style scoped lang="scss"></style>

+ 44 - 2
src/views/system/sector/index.vue

@@ -12,6 +12,24 @@
       <el-row>
         <el-col :span="24" v-if="dialog.type == '1'">
           <custom-form v-model="form" :fields="formFields" :rules="rules" @save="toSave">
+            <template #partner>
+              <el-button type="primary" size="mini" @click="addPartner()">添加</el-button>
+              <el-col :span="24" style="margin: 10px 0 0 0">
+                <el-table :data="form.partner" border size="mini">
+                  <el-table-column type="index" label="序号" width="60" align="center"> </el-table-column>
+                  <el-table-column prop="name" label="名称" align="center">
+                    <template v-slot="scope">
+                      <el-input v-model="scope.row.name" placeholder="请输入合作伙伴名称"></el-input>
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="操作" align="center" width="100">
+                    <template v-slot="scope">
+                      <el-button size="mini" type="danger" @click="delPartner(scope.row)">删除</el-button>
+                    </template>
+                  </el-table-column>
+                </el-table>
+              </el-col>
+            </template>
             <template #is_use>
               <el-radio v-for="i in isUseList" :key="i.id" :label="i.value">{{ i.label }}</el-radio>
             </template>
@@ -23,6 +41,7 @@
 </template>
 
 <script setup>
+import moment from 'moment'
 const $checkRes = inject('$checkRes')
 import { cloneDeep, get } from 'lodash-es'
 const { t } = useI18n()
@@ -36,6 +55,7 @@ const searchForm = ref({})
 const fields = [
   { label: t('pages.sector.title'), model: 'title', isSearch: true },
   { label: t('pages.sector.sort'), model: 'sort' },
+  { label: t('pages.sector.partner'), model: 'partner', format: (i) => getDict(i, 'partner') },
   {
     label: t('pages.sector.is_use'),
     model: 'is_use',
@@ -59,6 +79,7 @@ const formFields = [
   { label: t('pages.sector.title'), model: 'title' },
   { label: t('pages.sector.href'), model: 'href' },
   { label: t('pages.sector.English'), model: 'English' },
+  { label: t('pages.sector.partner'), model: 'partner', custom: true },
   { label: t('pages.sector.sort'), model: 'sort', type: 'number' },
   { label: t('pages.sector.is_use'), model: 'is_use', type: 'radio' },
   { label: t('pages.sector.remark'), model: 'remark', type: 'textarea' }
@@ -68,7 +89,7 @@ const rules = reactive({
   sort: [{ required: true, message: t('pages.sector.sortfMessage'), trigger: 'blur' }]
 })
 const dialog = ref({ type: '1', show: false, title: t('pages.sector.dialogTitle'), top: '15vh' })
-const form = ref({})
+const form = ref({ partner: [] })
 // 请求
 onMounted(async () => {
   loading.value = true
@@ -96,6 +117,12 @@ const getDict = (data, model) => {
   if (data) {
     let res
     if (model == 'is_use') res = isUseList.value.find((f) => f.value == data)
+    else if (model == 'partner') {
+      const partner = data.map((i) => {
+        return i.name
+      })
+      if (partner) return partner.join(',')
+    }
     return get(res, 'label')
   }
 }
@@ -115,6 +142,21 @@ const toDelete = async (data) => {
     search({ skip, limit })
   }
 }
+// 合作伙伴添加
+const addPartner = () => {
+  const partner = cloneDeep(form.value.partner) || []
+  if (partner && partner.length >= 5) {
+    this.$message({ message: `合作伙伴最多添加五个`, type: 'error' })
+  } else {
+    partner.push({ id: moment().valueOf(), name: '' })
+  }
+  form.value.partner = partner
+}
+// 合作伙伴删除
+const delPartner = (e) => {
+  let partner = form.value.partner.filter((i) => i.id != e.id)
+  form.value.partner = partner
+}
 const toSave = async () => {
   const data = cloneDeep(form.value)
   let res
@@ -131,7 +173,7 @@ const toReset = async () => {
   await search({ skip, limit })
 }
 const toClose = () => {
-  form.value = {}
+  form.value = { partner: [] }
   dialog.value = { show: false }
 }
 // 开启或禁用

+ 17 - 4
src/views/user/user/index.vue

@@ -1,6 +1,10 @@
 <template>
   <div class="main animate__animated animate__backInRight">
-    <custom-search-bar v-model="searchForm" :fields="fields.filter((f) => f.filter)" @search="search" @reset="toReset"></custom-search-bar>
+    <custom-search-bar v-model="searchForm" :fields="fields.filter((f) => f.filter)" @search="search" @reset="toReset">
+      <template #industry>
+        <el-option v-for="i in sectorList" :key="i.id" :label="i.title" :value="i.title"></el-option>
+      </template>
+    </custom-search-bar>
     <custom-table :data="data" :fields="fields" @search="search" :total="total" :opera="opera" @view="toView" @exam="toExam" @delete="toDelete">
       <template #role="{ row }">
         <div class="tags">
@@ -74,6 +78,8 @@ import { InvestmentStore } from '@/store/api/user/investment'
 const investmentStore = InvestmentStore()
 import { StateStore } from '@/store/api/user/state'
 const stateStore = StateStore()
+import { SectorStore } from '@/store/api/system/sector'
+const sectorStore = SectorStore()
 const { t } = useI18n()
 const loading = ref(false)
 let skip = 0
@@ -90,8 +96,8 @@ onMounted(async () => {
 })
 const fields = [
   { label: t('pages.user.account'), model: 'account', filter: true },
+  { label: t('pages.user.industry'), model: 'industry', filter: true, type: 'select' },
   { label: t('pages.user.nick_name'), model: 'nick_name', filter: true },
-  { label: t('pages.user.email'), model: 'email' },
   { label: t('pages.user.phone'), model: 'phone', filter: true },
   { label: t('pages.user.role'), model: 'role', custom: true },
   { label: t('pages.user.status'), model: 'status', format: (i) => getDict(i) }
@@ -141,6 +147,7 @@ const scaleList = ref([])
 const IndustryList = ref([])
 const cardTypeList = ref([])
 const contributionList = ref([])
+const sectorList = ref([])
 const searchOther = async () => {
   let result
   // 状态
@@ -181,6 +188,9 @@ const searchOther = async () => {
   // 角色
   result = await roleStore.query({ is_use: '0' })
   if ($checkRes(result)) roleList.value = result.data
+  // 行业
+  result = await sectorStore.query({ is_use: '0' })
+  if ($checkRes(result)) sectorList.value = result.data
 }
 
 const toDelete = async (data) => {
@@ -207,7 +217,8 @@ const toView = async (data) => {
 const toChang = async (name) => {
   let result
   if (name == 'User') {
-    result = await store.fetch(form.value.user)
+    const id = form.value.id || form.value.user
+    result = await store.fetch(id)
     if ($checkRes(result)) form.value = result.data
   } else {
     if (name == 'Expert') {
@@ -243,7 +254,8 @@ const onSubmit = async () => {
   else if (type.value == 'Investment') res = await investmentStore.update(data)
   else if (type.value == 'State') res = await stateStore.update(data)
   if ($checkRes(res, true)) {
-    toChang(type.value)
+    search({ skip, limit })
+    toClose()
   }
 }
 // 审核
@@ -277,6 +289,7 @@ provide('ruleFormRef ', ruleFormRef)
 provide('form', form)
 provide('onSubmit', onSubmit)
 // 字典
+provide('sectorList', sectorList)
 provide('statusList', statusList)
 provide('genderList', genderList)
 provide('fieldList', fieldList)

+ 8 - 2
src/views/user/user/parts/user.vue

@@ -1,13 +1,16 @@
 <template>
   <div class="index">
-    <custom-form v-model="form" :fields="formFields" :useSave="false">
+    <custom-form v-model="form" :fields="formFields" @save="onSubmit">
       <template #role>
         <div class="tags">
           <el-tag v-for="(item, index) in form.role" :key="index" type="primary">{{ getRole(item) }}</el-tag>
         </div>
       </template>
+      <template #industry>
+        <el-option v-for="i in sectorList" :key="i.id" :label="i.title" :value="i.title"></el-option>
+      </template>
       <template #gender>
-        <el-option disabled v-for="i in genderList" :key="i.id" :label="i.label" :value="i.value"></el-option>
+        <el-option v-for="i in genderList" :key="i.id" :label="i.label" :value="i.value"></el-option>
       </template>
     </custom-form>
   </div>
@@ -20,6 +23,7 @@ const getRole = inject('getRole')
 const formFields = ref([
   { label: t('pages.user.account'), model: 'account', options: { readonly: true } },
   { label: t('pages.user.nick_name'), model: 'nick_name', options: { readonly: true } },
+  { label: t('pages.user.industry'), model: 'industry', type: 'select' },
   { label: t('pages.user.gender'), model: 'gender', type: 'select' },
   { label: t('pages.user.email'), model: 'email', options: { readonly: true } },
   { label: t('pages.user.phone'), model: 'phone', options: { readonly: true } },
@@ -27,6 +31,8 @@ const formFields = ref([
 ])
 // 字典表
 const genderList = inject('genderList')
+const sectorList = inject('sectorList')
+const onSubmit = inject('onSubmit')
 </script>
 <style scoped lang="scss">
 .tags {