lrf 8 hónapja
szülő
commit
130d98c2d9

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

@@ -42,5 +42,6 @@ export default {
   incubator: '全省孵化基地情况',
   elevenHatch: '孵化基地情况',
   log: '日志管理',
-  log_opera: '操作日志'
+  log_opera: '操作日志',
+  system_message: '站内消息'
 }

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

@@ -438,5 +438,22 @@ export default {
     viewTabs3: '数据对比',
     key: '键名',
     value: '值'
+  },
+  message: {
+    content: '消息内容',
+    type: '消息类型',
+    to: '发送对象',
+    view: '查看接收情况',
+    contentRuleMessage: '请输入消息内容',
+    typeRuleMessage: '请选择消息类型',
+    dialogTitle: '消息详情',
+    addDialogTitle: '消息添加',
+    inputNamePla: '请输入要发送的用户名称',
+    to_user: '接收用户名称',
+    has_user: '已添加该用户',
+    is_read: '是否已读',
+    viewTitle: '接收情况',
+    remind: '重提示',
+    remindSuccess: '重提示成功'
   }
 }

+ 45 - 0
src/store/api/system/message.js

@@ -0,0 +1,45 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/message'
+const axios = new AxiosWrapper()
+
+export const MessageStore = defineStore('message', () => {
+  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
+  }
+  const remind = async (payload) => {
+    const res = await axios.$post(`${url}/remind/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    create,
+    update,
+    del,
+    remind
+  }
+})

+ 0 - 1
src/utils/stomp.js

@@ -23,7 +23,6 @@ const initClient = (subscribes) => {
     console.log('is connected')
     if (!isObject(subscribes)) return
     for (const url in subscribes) {
-      console.log(url)
       const cb = subscribes[url]
       client.subscribe(url, (msg) => {
         let body = get(msg, 'body')

+ 146 - 0
src/views/system/message/index.vue

@@ -0,0 +1,146 @@
+<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"></custom-search-bar>
+    <custom-button-bar :fields="buttonFields" @create="toAdd"></custom-button-bar>
+    <custom-table :data="data" :fields="fields" @query="search" :total="total" :opera="opera" @remind="toRemind" @view="toView">
+      <template #is_use="{ row }">
+        <el-tag v-if="row.is_use == '0'" type="success">{{ $t('common.is_use_abled') }}</el-tag>
+        <el-tag v-else type="info">{{ $t('common.is_use_disabled') }}</el-tag>
+      </template>
+    </custom-table>
+    <el-dialog v-model="dialog.show" :title="dialog.title" :destroy-on-close="false" @close="toClose" :top="dialog.top">
+      <el-row>
+        <el-col :span="24" v-if="dialog.type == '1'">
+          <custom-form v-model="form" :fields="formFields" :rules="rules" @save="toSave">
+            <template #type>
+              <el-radio v-for="i in msgTypeList" :key="i.id" :label="i.value">{{ i.label }}</el-radio>
+            </template>
+            <template #to>
+              <to-select v-model="form"></to-select>
+            </template>
+          </custom-form>
+        </el-col>
+        <el-col :span="24" v-else="dialog.type == '2'">
+          <to-view-com :data="viewData"></to-view-com>
+        </el-col>
+      </el-row>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import toSelect from './parts/to.vue'
+import toViewCom from './parts/view.vue'
+const $checkRes = inject('$checkRes')
+import { cloneDeep, get } from 'lodash-es'
+const { t } = useI18n()
+// 接口
+import { MessageStore } from '@/store/api/system/message'
+import { DictDataStore } from '@/store/api/system/dictData'
+const store = MessageStore()
+const dictDataStore = DictDataStore()
+const data = ref([])
+const searchForm = ref({})
+const viewData = ref([])
+const fields = [
+  { label: t('pages.message.content'), model: 'content' },
+  { label: t('pages.message.type'), model: 'type', format: (i) => getDict(i) }
+]
+const opera = [
+  { label: t('pages.message.view'), method: 'view', type: 'primary' },
+  { label: t('pages.message.remind'), method: 'remind', type: 'primary' }
+]
+const buttonFields = [{ label: t('common.create'), method: 'create' }]
+let skip = 0
+let limit = inject('limit')
+const total = ref(0)
+const isReadList = ref([])
+const msgTypeList = ref([])
+// 加载中
+const loading = ref(false)
+const formFields = [
+  { label: t('pages.message.content'), model: 'content', type: 'textarea', options: { rows: 4 } },
+  { label: t('pages.message.type'), model: 'type', type: 'radio' },
+  { label: t('pages.message.to'), model: 'to', custom: true }
+]
+const rules = reactive({ content: [{ required: true, message: t('pages.message.contentRuleMessage'), trigger: 'blur' }], type: [{ required: true, message: t('pages.message.typeRuleMessage'), trigger: 'blur' }] })
+const dialog = ref({ type: '1', show: false, title: t('pages.message.dialogTitle'), top: '15vh' })
+const form = ref({})
+// 请求
+onMounted(async () => {
+  loading.value = true
+  await searchOther()
+  await search({ skip, limit })
+  loading.value = false
+})
+
+const searchOther = async () => {
+  let result = await dictDataStore.query({ code: 'messageIsRead', is_use: '0' })
+  if ($checkRes(result)) isReadList.value = result.data
+  result = await dictDataStore.query({ code: 'messageType', is_use: '0' })
+  if ($checkRes(result)) msgTypeList.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) => {
+  const res = msgTypeList.value.find((f) => f.value == data)
+  return get(res, 'label')
+}
+const toRemind = async (data) => {
+  const id = get(data, 'id')
+  if (!id) return
+  const res = await store.remind(id)
+  $checkRes(res, t('pages.message.remindSuccess'))
+}
+
+// 查看发送对象
+const toView = (data) => {
+  let values = get(data, 'to', [])
+  values = values.map((i) => {
+    const is_read = isReadList.value.find((f) => f.value === i.is_read)
+    if (is_read) i.is_read = get(is_read, 'label')
+    return i
+  })
+  viewData.value = values
+  dialog.value = { type: '2', show: true, title: t('pages.message.viewTitle'), top: '15vh' }
+}
+// 添加
+const toAdd = () => {
+  dialog.value = { type: '1', show: true, title: t('pages.message.addDialogTitle'), top: '15vh' }
+  form.value.to = []
+  form.value.type = '0'
+}
+
+const toSave = async () => {
+  const data = cloneDeep(form.value)
+  let res
+  if (get(data, 'id')) res = await store.update(data)
+  else res = await store.create(data)
+  if ($checkRes(res, true)) {
+    search({ skip, limit })
+    toClose()
+  }
+}
+// 重置
+const toReset = async () => {
+  searchForm.value = {}
+  await search({ skip, limit })
+}
+const toClose = () => {
+  form.value = {}
+  dialog.value = { show: false }
+}
+// provide
+provide('isReadList', isReadList)
+provide('codeInfo', form)
+</script>
+<style scoped lang="scss"></style>

+ 72 - 0
src/views/system/message/parts/to.vue

@@ -0,0 +1,72 @@
+<template>
+  <el-row>
+    <el-col :span="24">
+      <el-autocomplete v-model="inputName" :fetch-suggestions="querySearchAsync" :placeholder="$t('pages.message.inputNamePla')" @select="handleSelect">
+        <template #default="{ item }">
+          {{ item.nick_name }}
+        </template>
+      </el-autocomplete>
+    </el-col>
+  </el-row>
+  <el-table :data="form.to">
+    <el-table-column align="center" :label="$t('pages.message.to_user')" prop="user_nick_name"></el-table-column>
+    <el-table-column align="center" :label="$t('common.opera')">
+      <template #="{ row }">
+        <el-button text type="danger" @click="clean(row)">删除</el-button>
+      </template>
+    </el-table-column>
+  </el-table>
+</template>
+
+<script setup>
+import { UserStore } from '@/store/api/user/user'
+import { ElMessage } from 'element-plus'
+import { get } from 'lodash-es'
+const props = defineProps({
+  modelValue: { type: Object }
+})
+const emits = defineEmits(['update:modelValue'])
+const form = computed({
+  get() {
+    return props.modelValue
+  },
+  set(value) {
+    emits('update:modelValue', value)
+  }
+})
+const $checkRes = inject('$checkRes')
+const { t } = useI18n()
+const inputName = ref()
+const store = UserStore()
+
+const querySearchAsync = async (qs, cb) => {
+  let list = []
+  const result = await store.query({ nick_name: qs })
+  if ($checkRes(result)) list = result.data
+  cb(list)
+}
+
+const handleSelect = (item) => {
+  const obj = {
+    user: get(item, 'id'),
+    user_nick_name: get(item, 'nick_name'),
+    user_type: 'user',
+    is_read: '0'
+  }
+  const value = get(form, 'value.to', [])
+  const has = value.find((f) => f.user === obj.user)
+  if (has) {
+    ElMessage({
+      message: t('pages.message.has_user'),
+      type: 'warning'
+    })
+    return
+  }
+  form.value.to.push(obj)
+}
+const clean = (data) => {
+  const i = form.value.to.findIndex((f) => f.user === data.user)
+  if (i >= 0) form.value.to.splice(i, 1)
+}
+</script>
+<style scoped></style>

+ 13 - 0
src/views/system/message/parts/view.vue

@@ -0,0 +1,13 @@
+<template>
+  <el-table :data="data" max-height="50vh">
+    <el-table-column align="center" :label="$t('pages.message.to_user')" prop="user_nick_name"></el-table-column>
+    <el-table-column align="center" :label="$t('pages.message.is_read')" prop="is_read"></el-table-column>
+  </el-table>
+</template>
+
+<script setup>
+const props = defineProps({
+  data: { type: Array }
+})
+</script>
+<style scoped></style>