YY 2 سال پیش
والد
کامیت
a8a1ca1dd8

+ 3 - 3
src/components/c-search.vue

@@ -11,7 +11,7 @@
         </el-col>
       </el-col>
       <el-col :span="24" class="search" v-if="is_search">
-        <el-form ref="form" :model="form" label-width="auto">
+        <el-form ref="formRef" :model="form" label-width="auto">
           <el-col :span="24" class="form">
             <template v-for="(item, index) in fields">
               <el-col :span="8" class="form_1" :key="'form-field-' + index" v-if="item.isSearch == true">
@@ -89,9 +89,9 @@ const { tip } = toRefs(props);
 const { remark } = toRefs(props);
 
 let form: Ref<{}> = ref({});
-const emit = defineEmits(['search', 'toReset', 'toBack','dataChange']);
+const emit = defineEmits(['search', 'toReset', 'toBack', 'dataChange']);
 const toSubmit = () => {
-  const obj = _.pickBy(form);
+  const obj = _.pickBy(form.value);
   emit('search', { ...obj });
 };
 // 重置

+ 1 - 1
src/components/c-upload.vue

@@ -44,7 +44,7 @@ const props = defineProps({
   url: { type: String, default: () => '' },
   limit: { type: Number, default: () => 6 },
   accept: { type: String, default: () => 'image/png, image/jpeg' },
-  listType: { type: String, default: () => 'text' },
+  listType: { type: String, default: () => 'text' }, //'text' | 'picture' | 'picture-card'
   tip: { type: String, default: () => undefined },
   list: { type: Array<ListItem>, default: () => [] },
   model: { type: String, default: () => '' },

+ 3 - 1
src/components/file-1.vue

@@ -4,7 +4,7 @@
       <el-col :span="24" class="main">
         <el-form :model="form" ref="formRef" label-width="auto">
           <el-form-item label="佐证资料" prop="file">
-            <component :is="CUpload" :limit="limit" :url="url" :list="form.file" @change="onChange" style="width: 100%"></component>
+            <component :is="CUpload" :limit="limit" :model="model" :url="url" :list="form.file" @change="onChange" style="width: 100%"></component>
           </el-form-item>
           <el-col :span="24" class="btn" v-if="!noEdit">
             <el-button type="primary" @click="onSubmit()">确定</el-button>
@@ -28,12 +28,14 @@ const formRef = ref<FormInstance>();
 // #region 参数传递
 const props = defineProps({
   form: { type: Object, default: () => {} },
+  model: { type: String, default: () => '' },
   noEdit: { type: Boolean, default: () => false },
   url: { type: String, default: () => '/files/freeLabel/upload' },
 });
 const { form } = toRefs(props);
 const { noEdit } = toRefs(props);
 const { url } = toRefs(props);
+const { model } = toRefs(props);
 // #endregion
 
 const emit = defineEmits(['onSubmit', 'resetForm']);

+ 42 - 34
src/router/index.ts

@@ -40,40 +40,48 @@ router.beforeEach((to, from, next) => {
   if (token) {
     // let user = jwt.decode(token);
     const user = {
-      _id: '640a8f0d9dfe6bbfaba9880a',
-      nick_name: '个人账号',
-      unit: '63b511fa2f41cd4838e478bb',
-      role: ['63b64ac735df6c6862e0eba3', '63b64b6335df6c6862e13f14', '63b62d2a9bb09c9905e19063'],
-      role_type: '2',
-      email: 'guhongwei0324@163.com',
-      phone: '13174420325',
-      unit_address: '长春市朝阳区前进大街1244号',
-      exam_status: '1',
-      card: '220182199603257019',
-      basic_id: '640aa44061ebe6235411e9db',
-      entrances: {
-        web: {
-          is_use: false,
-        },
-        project: {
-          is_use: true,
-          url: 'http://jcdtgl.waityou24.cn/project',
-        },
-        admin: {
-          is_use: true,
-          url: 'http://jcdtgl.waityou24.cn/admin',
-        },
-        basic: {
-          is_use: true,
-          url: 'http://jcdtgl.waityou24.cn/basic',
-        },
-        studio: {
-          is_use: true,
-          url: 'http://jcdtgl.waityou24.cn/studio',
-        },
-      },
-      iat: 1678686391,
-      exp: 1678859191,
+      _id: '63b3ed3e404c08bbd8539965',
+      account: 'admin',
+      name: '管理员',
+      role: ['63b641cf35df6c6862df5d39', '63b628199bb09c9905def4cc', '63b64b1f35df6c6862e11bf2', '63b62cac9bb09c9905e14857'],
+      is_super: false,
+      role_type: '1',
+      iat: 1679041521,
+      exp: 1679214321,
+      // _id: '640a8f0d9dfe6bbfaba9880a',
+      // nick_name: '个人账号',
+      // unit: '63b511fa2f41cd4838e478bb',
+      // role: ['63b64ac735df6c6862e0eba3', '63b64b6335df6c6862e13f14', '63b62d2a9bb09c9905e19063'],
+      // role_type: '2',
+      // email: 'guhongwei0324@163.com',
+      // phone: '13174420325',
+      // unit_address: '长春市朝阳区前进大街1244号',
+      // exam_status: '1',
+      // card: '220182199603257019',
+      // basic_id: '640aa44061ebe6235411e9db',
+      // entrances: {
+      //   web: {
+      //     is_use: false,
+      //   },
+      //   project: {
+      //     is_use: true,
+      //     url: 'http://jcdtgl.waityou24.cn/project',
+      //   },
+      //   admin: {
+      //     is_use: true,
+      //     url: 'http://jcdtgl.waityou24.cn/admin',
+      //   },
+      //   basic: {
+      //     is_use: true,
+      //     url: 'http://jcdtgl.waityou24.cn/basic',
+      //   },
+      //   studio: {
+      //     is_use: true,
+      //     url: 'http://jcdtgl.waityou24.cn/studio',
+      //   },
+      // },
+      // iat: 1678686391,
+      // exp: 1678859191,
     };
     store.commit('setUser', user, { root: true });
     next();

+ 177 - 0
src/views/center/other/achieve/index.vue

@@ -0,0 +1,177 @@
+<template>
+  <div id="index">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight">
+        <el-col :span="24" class="one">
+          <c-search :is_search="true" :fields="fields" @search="btSearch">
+            <template #status>
+              <el-option v-for="i in statusList" :key="i.model" :label="i.dict_label" :value="i.dict_value"></el-option>
+            </template>
+          </c-search>
+        </el-col>
+        <el-col :span="24" class="thr">
+          <data-table :fields="fields" :opera="opera" @query="search" :data="list" :total="total" @view="toView" @exam="toExam"> </data-table>
+        </el-col>
+      </el-col>
+    </el-row>
+    <c-dialog :dialog="dialog" @toClose="toClose">
+      <template v-slot:info>
+        <el-col :span="24" class="dialog_one" v-if="dialog.type == '1'">
+          <el-form :model="form" :rules="rules" ref="form" label-width="auto">
+            <el-form-item label="审核状态" prop="status">
+              <el-select v-model="form.status" clearable filterable placeholder="请选择审核状态" style="width: 100%">
+                <el-option v-for="(item, index) in statusList" :key="index" :label="item.dict_label" :value="item.dict_value"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item label="审核意见" prop="remark">
+              <el-input v-model="form.remark" placeholder="请输入审核意见" type="textarea"></el-input>
+            </el-form-item>
+            <el-form-item>
+              <el-button size="small" type="primary" @click="toSubmit('form')">提交审核</el-button>
+            </el-form-item>
+          </el-form>
+        </el-col>
+      </template>
+    </c-dialog>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions } = createNamespacedHelpers('achieve');
+const { mapActions: sysdictdata } = createNamespacedHelpers('sysdictdata');
+const { mapActions: message } = createNamespacedHelpers('message');
+const { mapActions: unitStudioApply } = createNamespacedHelpers('unitStudioApply');
+const moment = require('moment');
+export default {
+  name: 'index',
+  props: {},
+  components: {},
+  data: function () {
+    return {
+      // 查询
+      searchInfo: {},
+      list: [],
+      total: 0,
+      fields: [
+        { label: '序号', options: { type: 'index' } },
+        { label: '标题', model: 'title', isSearch: true },
+        { label: '成果取得时间', model: 'get_time' },
+        { label: '依托单位名称', model: 'company_name', isSearch: true },
+        {
+          label: '审核状态',
+          model: 'status',
+          type: 'select',
+          format: (i) => {
+            let data = this.statusList.find((r) => r.dict_value == i);
+            if (data) return data.dict_label;
+          },
+          isSearch: true,
+        },
+      ],
+      opera: [
+        { label: '详情', method: 'view' },
+        { label: '审核', method: 'exam', type: 'warning', display: (i) => i.status == '0' },
+      ],
+      // 审核状态
+      statusList: [],
+      dialog: { title: '信息审核', show: false, type: '1' },
+      form: {},
+      rules: {
+        status: [{ required: true, message: '请选择审核状态', trigger: 'change' }],
+        remark: [{ required: true, message: '请输入审核意见', trigger: 'blur' }],
+      },
+    };
+  },
+  created() {
+    this.searchOther();
+    this.search();
+  },
+  methods: {
+    ...mapActions(['query', 'update', 'delete']),
+    ...sysdictdata({ dQuery: 'query' }),
+    ...message({ mCreate: 'create' }),
+    ...unitStudioApply({ CFetch: 'fetch' }),
+    async search({ skip = 0, limit = this.$limit, ...info } = {}) {
+      let res = await this.query({ skip, limit, ...info, ...this.searchInfo });
+      if (this.$checkRes(res)) {
+        this.$set(this, `list`, res.data);
+        this.$set(this, `total`, res.total);
+      }
+    },
+    btSearch(query) {
+      this.$set(this, `searchInfo`, query);
+      this.search();
+    },
+    // 详细信息
+    toView({ data }) {
+      this.$router.push({ path: '/center/other/achieve/info', query: { id: data.id } });
+    },
+    // 信息审核
+    toExam({ data }) {
+      this.$set(this, `form`, data);
+      this.dialog = { title: '信息审核', show: true, type: '1' };
+    },
+    // 提交审核
+    toSubmit(formName) {
+      this.$refs[formName].validate(async (valid) => {
+        if (valid) {
+          let data = this.form;
+          let obj = { id: data.id, status: data.status };
+          let res = await this.update(obj);
+          if (this.$checkRes(res, `信息审核成功`, res.errmsg)) this.createMess(data);
+        } else {
+          console.log('error submit!!');
+          return false;
+        }
+      });
+    },
+    // 发送系统消息
+    async createMess(data) {
+      let res = await this.CFetch(data.user_id);
+      if (this.$checkRes(res)) {
+        let obj = {
+          user_id: this.user._id,
+          title: '审核通知',
+          send_time: moment().format('YYYY-MM-DD HH:mm:ss'),
+          type: '3',
+          user: [{ id: data.user_id, company: data.company_name, phone: res.data.phone }],
+          content: '您好,您所发布的《' + data.title + '》的申请,' + `${data.status == '1' ? '已通过审核' : '未通过审核'}` + ',原因:' + data.remark,
+        };
+        let arr = await this.mCreate(obj);
+        if (this.$checkRes(arr, `系统信息发送成功`, arr.errmsg)) this.toClose();
+      }
+    },
+    // 关闭弹框
+    toClose() {
+      this.form = {};
+      this.dialog = { title: '信息审核', show: false, type: '1' };
+      this.search();
+    },
+    // 查詢其他信息
+    async searchOther() {
+      let res;
+      // 审核状态
+      res = await this.dQuery({ dict_type: 'studio_status' });
+      if (this.$checkRes(res)) {
+        this.$set(this, `statusList`, res.data);
+      }
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 67 - 0
src/views/center/other/achieve/info.vue

@@ -0,0 +1,67 @@
+<template>
+  <div id="info">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight">
+        <el-col :span="24" class="one">
+          <c-search :is_back="true" @toBack="toBack()"></c-search>
+        </el-col>
+        <el-col :span="24" class="two">
+          <achieve-info :info="info"></achieve-info>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions } = createNamespacedHelpers('achieve');
+export default {
+  name: 'info',
+  props: {},
+  components: {
+    achieveInfo: () => import('@c/common/achieve/info.vue'),
+  },
+  data: function () {
+    return {
+      info: {},
+    };
+  },
+  created() {
+    this.search();
+  },
+  methods: {
+    ...mapActions(['fetch']),
+    async search() {
+      if (this.id) {
+        let res = await this.fetch(this.id);
+        if (this.$checkRes(res)) {
+          this.$set(this, `info`, res.data);
+        }
+      }
+    },
+    // 返回
+    toBack() {
+      window.history.go('-1');
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    id() {
+      return this.$route.query.id;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 76 - 0
src/views/center/other/contact/index.vue

@@ -0,0 +1,76 @@
+<template>
+  <div id="add">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight">
+        <el-col :span="24" class="one">
+          <component :is="partsSearch"></component>
+        </el-col>
+        <el-col :span="24" class="two">
+          <component :is="CForm" :fields="infoFields" :rules="rules" :form="form" labelWidth="auto" @save="toSave">
+            <template #is_use>
+              <el-option v-for="i in isuseList" :key="i.model" :label="i.dict_label" :value="i.dict_value"></el-option>
+            </template>
+            <template #content>
+              <component :is="WangEditor" v-model="form.content" url="/files/studioadmin/other/upload"></component>
+            </template>
+          </component>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+<script setup lang="ts">
+import partsSearch from '@/components/c-search.vue';
+import CForm from '@/components/c-form.vue';
+import WangEditor from '@/components/wang-editor.vue';
+import store from '@/stores/counter';
+import type { Ref } from 'vue';
+import { ref, onMounted, reactive } from 'vue';
+import type { FormRules } from 'element-plus';
+import { ElMessage } from 'element-plus';
+import { ContactofficeStore } from '@common/src/stores/studio/other/contactoffice'; // 列表 // 列表
+import { DictDataStore } from '@common/src/stores/users/sysdictdata'; // 字典表
+import type { IQueryResult } from '@/util/types.util';
+const contactoffice = ContactofficeStore();
+const sysdictdata = DictDataStore();
+// 表单
+let form: Ref<{ content: string }> = ref({ content: '' });
+// 必填项
+const rules = reactive<FormRules>({
+  is_use: [{ required: true, message: '请选择是否启用', trigger: 'change' }],
+  content: [{ required: true, message: '请输入信息内容', trigger: 'blur' }],
+});
+// 表单
+let infoFields: Ref<any[]> = ref([
+  { label: '是否启用', model: 'is_use', type: 'select' },
+  { label: '信息内容', model: 'content', custom: true },
+]);
+let isuseList: Ref<any[]> = ref([]);
+
+let user = store.state.user;
+onMounted(async () => {
+  await searchOther();
+  await search();
+});
+const search = async () => {
+  const res: IQueryResult = await contactoffice.query();
+  form.value = res.data[0] as { content: string };
+};
+// 提交
+const toSave = async (data: { _id: string; user_id: string }) => {
+  data.user_id = user.id;
+  let res: IQueryResult;
+  if (data._id) res = await contactoffice.update(data);
+  else res = await contactoffice.create(data);
+  if (res.errcode == 0) ElMessage({ type: 'success', message: '维护信息成功' });
+  else ElMessage({ type: 'warning', message: `${res.errmsg}` });
+};
+// 查询其他信息
+const searchOther = async () => {
+  // 字典表---审核状态
+  const p1: IQueryResult = await sysdictdata.query({ dict_type: 'sys_yes_no' });
+  isuseList.value = p1.data as [];
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 99 - 0
src/views/center/other/download/add.vue

@@ -0,0 +1,99 @@
+<template>
+  <div id="add">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight">
+        <el-col :span="24" class="one">
+          <component :is="partsSearch" :is_back="true" @toBack="toBack()"></component>
+        </el-col>
+        <el-col :span="24" class="two">
+          <component :is="CForm" :fields="infoFields" :rules="rules" :form="form" labelWidth="auto" @save="toSave">
+            <template #is_use>
+              <el-option v-for="i in isuseList" :key="i.model" :label="i.dict_label" :value="i.dict_value"></el-option>
+            </template>
+            <template #file>
+              <component :is="CUpload" model="file" :limit="limit" :url="url" :list="form.file" @change="onChange"></component>
+            </template>
+            <template #content>
+              <component :is="WangEditor" v-model="form.content" url="/files/studioadmin/other/upload"></component>
+            </template>
+          </component>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+<script setup lang="ts">
+import partsSearch from '@/components/c-search.vue';
+import CForm from '@/components/c-form.vue';
+import WangEditor from '@/components/wang-editor.vue';
+import CUpload from '@/components/c-upload.vue';
+import { useRoute } from 'vue-router';
+import type { Ref } from 'vue';
+import { ref, onMounted, reactive } from 'vue';
+import type { FormRules } from 'element-plus';
+import { ElMessage } from 'element-plus';
+import { RelevantdownloadStore } from '@common/src/stores/studio/other/relevantdownload'; // 列表 // 列表
+import { DictDataStore } from '@common/src/stores/users/sysdictdata'; // 字典表
+import type { IQueryResult } from '@/util/types.util';
+const relevantdownload = RelevantdownloadStore();
+const sysdictdata = DictDataStore();
+interface dataItem {}
+let route = useRoute();
+// 表单
+let form: Ref<{ content: string; file: dataItem[] }> = ref({ content: '', file: [] });
+// 必填项
+const rules = reactive<FormRules>({
+  title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
+  date: [{ required: true, message: '请选择发布时间', trigger: 'change' }],
+  origin: [{ required: true, message: '请输入信息来源', trigger: 'change' }],
+});
+// 表单
+let infoFields: Ref<any[]> = ref([
+  { label: '标题', model: 'title' },
+  { label: '发布时间', model: 'date', type: 'date' },
+  { label: '信息来源', model: 'origin' },
+  { label: '信息文件', model: 'file', custom: true },
+  { label: '信息内容', model: 'content', custom: true },
+  { label: '是否启用', model: 'is_use', type: 'select' },
+]);
+let isuseList: Ref<any[]> = ref([]);
+
+let limit: Ref<number> = ref(1);
+let url: Ref<string> = ref('/files/studioadmin/other/upload');
+onMounted(async () => {
+  await searchOther();
+  await search();
+});
+const search = async () => {
+  if (route.query.id) {
+    let id = route.query.id;
+    const res: IQueryResult = await relevantdownload.fetch(id);
+    form.value = res.data as { content: string; file: dataItem[] };
+  }
+};
+const onChange = (e: { model: string; value: Array<dataItem> }) => {
+  const { model, value } = e;
+  form.value[model] = value;
+};
+// 提交
+const toSave = async (data: { _id: string }) => {
+  let res: IQueryResult;
+  if (data._id) res = await relevantdownload.update(data);
+  else res = await relevantdownload.create(data);
+  if (res.errcode == 0) {
+    ElMessage({ type: 'success', message: '维护信息成功' });
+    toBack();
+  } else ElMessage({ type: 'warning', message: `${res.errmsg}` });
+};
+const toBack = () => {
+  window.history.go(-1);
+};
+// 查询其他信息
+const searchOther = async () => {
+  // 字典表---审核状态
+  const p1: IQueryResult = await sysdictdata.query({ dict_type: 'sys_yes_no' });
+  isuseList.value = p1.data as [];
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 140 - 0
src/views/center/other/download/index.vue

@@ -0,0 +1,140 @@
+<template>
+  <div id="index">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight">
+        <el-col :span="24" class="one">
+          <component :is="partsSearch" :is_search="true" :fields="fields" @search="partSearch">
+            <template #is_use>
+              <el-option v-for="i in isuseList" :key="i.model" :label="i.dict_label" :value="i.dict_value"></el-option>
+            </template>
+          </component>
+        </el-col>
+        <el-col :span="24" class="two">
+          <component :is="btn" @toAdd="toAdd"></component>
+        </el-col>
+        <el-col :span="24" class="thr">
+          <component
+            :is="CTable"
+            :fields="fields"
+            :opera="opera"
+            :select="false"
+            :selected="selected"
+            @handleSelect="handleSelect"
+            @query="search"
+            :data="tableData"
+            :total="total"
+            @edit="toEdit"
+            @del="toDel"
+          >
+          </component>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+<script setup lang="ts">
+// #region 组件
+import partsSearch from '@/components/c-search.vue';
+import CTable from '@/components/c-table.vue';
+import btn from '@/components/btn-1.vue';
+// #endregion
+import type { Ref } from 'vue';
+import { ref, onMounted, getCurrentInstance } from 'vue';
+
+import { ElMessage } from 'element-plus';
+import { useRouter } from 'vue-router';
+// #region 接口
+import { RelevantdownloadStore } from '@common/src/stores/studio/other/relevantdownload'; // 列表 // 列表
+import { DictDataStore } from '@common/src/stores/users/sysdictdata'; // 字典表
+import type { IQueryResult } from '@/util/types.util';
+const relevantdownload = RelevantdownloadStore();
+const sysdictdata = DictDataStore();
+const { proxy } = getCurrentInstance() as any;
+const router = useRouter();
+// #endregion
+// 列表数据
+let tableData: Ref<any[]> = ref([]);
+// 列表
+let fields: Ref<any[]> = ref([
+  { label: '序号', options: { type: 'index' } },
+  { label: '标题', model: 'title', isSearch: true },
+  { label: '发布时间', model: 'date' },
+  { label: '信息来源', model: 'origin', isSearch: true },
+  {
+    label: '是否启用',
+    model: 'is_use',
+    type: 'select',
+    format: (i) => {
+      let data = isuseList.value.find((r) => r.dict_value == i);
+      if (data) return data.dict_label;
+    },
+    isSearch: true,
+  },
+]);
+// 操作
+let opera: Ref<any[]> = ref([
+  { label: '修改', method: 'edit' },
+  { label: '删除', method: 'del', confirm: true, type: 'danger' },
+]);
+// 多选
+let selected: Ref<any[]> = ref([]);
+// 总数
+let total: Ref<number> = ref(0);
+let skip = 0;
+let limit: number = proxy.$limit;
+// 查询数据
+let searchForm: Ref<{}> = ref({});
+// 状态
+let isuseList: Ref<any[]> = ref([]);
+onMounted(async () => {
+  await searchOther();
+  await search({ skip, limit });
+});
+// 查询
+const search = async (e: { skip: number; limit: number }) => {
+  const { skip, limit } = e;
+  let info = { limit: limit, skip: skip, ...searchForm.value, is_use: 'Y' };
+  const res: IQueryResult = await relevantdownload.query(info);
+  tableData.value = res.data as any[];
+  total.value = res.total;
+};
+// 查询
+const partSearch = (form: { [x: string]: any }) => {
+  searchForm.value = form;
+  search({ skip, limit });
+};
+// 添加
+const toAdd = () => {
+  router.push({ path: '/center/other/download/add' });
+};
+// 修改
+const toEdit = (data) => {
+  router.push({ path: '/center/other/download/add', query: { id: data._id } });
+};
+
+// 删除
+const toDel = async (data: { _id: string }) => {
+  const res: IQueryResult = await relevantdownload.del(data._id);
+  if (res.errcode == 0) {
+    ElMessage({ type: 'success', message: '删除成功' });
+    search({ skip, limit });
+  }
+};
+
+// 选择
+const handleSelect = () => {};
+// 查询其他信息
+const searchOther = async () => {
+  // 字典表---审核状态
+  const p1: IQueryResult = await sysdictdata.query({ dict_type: 'sys_yes_no' });
+  isuseList.value = p1.data as [];
+};
+</script>
+<style lang="scss" scoped>
+.one {
+  margin: 0 0 10px 0;
+}
+.two {
+  margin: 0 0 10px 0;
+}
+</style>

+ 259 - 0
src/views/center/other/message/add.vue

@@ -0,0 +1,259 @@
+<template>
+  <div id="add">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight">
+        <el-col :span="24" class="one">
+          <component :is="partsSearch" :is_back="true" @toBack="toBack()"></component>
+        </el-col>
+        <el-col :span="24" class="two">
+          <component :is="CForm" :fields="infoFields" :rules="rules" :form="form" labelWidth="auto" @save="toSave">
+            <template #type>
+              <el-option v-for="i in typeList" :key="i.model" :label="i.dict_label" :value="i.dict_value"></el-option>
+            </template>
+            <template #user>
+              <el-select clearable filterable multiple collapse-tags v-model="form.user" placeholder="请选择" style="width: 100%">
+                <el-option v-for="item in userList" :key="item.id" :label="item.name || item.company" :value="item.id">
+                  <span style="float: left">{{ item.name || item.company }}</span>
+                  <span style="float: right; color: #8492a6; font-size: 13px">{{ item.name ? '科学家' : '依托单位' }}</span>
+                </el-option>
+              </el-select>
+            </template>
+            <template #file>
+              <component :is="CUpload" model="file" :limit="limit" :url="url" :list="form.file" @change="onChange"></component>
+            </template>
+            <template #content>
+              <component :is="WangEditor" v-model="form.content" url="/files/studioadmin/other/upload"></component>
+            </template>
+          </component>
+          <!-- <data-form :fields="fields" :form="form" :rules="rules" @save="toSave" @dataChange="dataChange" :span="24">
+            <template #type>
+              <el-option v-for="i in typeList" :key="i.model" :label="i.dict_label" :value="i.dict_value"></el-option>
+            </template>
+            <template #user>
+              <el-select clearable filterable multiple collapse-tags v-model="form.user" placeholder="请选择" style="width: 100%">
+                <el-option v-for="item in userList" :key="item.id" :label="item.name || item.company" :value="item.id">
+                  <span style="float: left">{{ item.name || item.company }}</span>
+                  <span style="float: right; color: #8492a6; font-size: 13px">{{ item.name ? '科学家' : '依托单位' }}</span>
+                </el-option>
+              </el-select>
+            </template>
+            <template #file="{ item }">
+              <c-upload v-model="form[item.model]" url="/files/studioadmin/other/upload" accept="" listType="text" :limit="1"></c-upload>
+            </template>
+            <template #content>
+              <editor v-model="form.content" url="/files/studioadmin/other/upload"></editor>
+            </template>
+          </data-form> -->
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+<script setup lang="ts">
+import partsSearch from '@/components/c-search.vue';
+import CForm from '@/components/c-form.vue';
+import WangEditor from '@/components/wang-editor.vue';
+import CUpload from '@/components/c-upload.vue';
+import { useRoute } from 'vue-router';
+import type { Ref } from 'vue';
+import { ref, onMounted, reactive } from 'vue';
+import type { FormRules } from 'element-plus';
+import { ElMessage } from 'element-plus';
+import { MessageStore } from '@common/src/stores/studio/other/message'; // 列表 // 列表
+import { DictDataStore } from '@common/src/stores/users/sysdictdata'; // 字典表
+import type { IQueryResult } from '@/util/types.util';
+const message = MessageStore();
+const sysdictdata = DictDataStore();
+interface dataItem {}
+let route = useRoute();
+// 表单
+let form: Ref<{ content: string; file: dataItem[]; user: dataItem[] }> = ref({ content: '', file: [], user: [] });
+// 必填项
+const rules = reactive<FormRules>({
+  title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
+  send_time: [{ required: true, message: '请选择发送时间', trigger: 'change' }],
+  type: [{ required: true, message: '请选择接收人类型', trigger: 'change' }],
+  file: [{ required: true, message: '信息文件', trigger: 'change' }],
+  content: [{ required: true, message: '信息内容', trigger: 'change' }],
+});
+// 表单
+let infoFields: Ref<any[]> = ref([
+  { label: '标题', model: 'title' },
+  { label: '发送时间', model: 'send_time', type: 'datetime' },
+  { label: '接收人类型', model: 'type', type: 'select' },
+  { label: '信息文件', model: 'file', custom: true },
+  { label: '信息内容', model: 'content', custom: true },
+]); // 接收人类型
+let typeList: Ref<any[]> = ref([]);
+// 接收人
+let userList: Ref<any[]> = ref([]);
+
+let limit: Ref<number> = ref(1);
+let url: Ref<string> = ref('/files/studioadmin/other/upload');
+onMounted(async () => {
+  await searchOther();
+  await search();
+});
+const search = async () => {
+   let form = { user_id: this.user.id };
+      this.$set(this, `form`, form);
+};
+const onChange = (e: { model: string; value: Array<dataItem> }) => {
+  const { model, value } = e;
+  form.value[model] = value;
+};
+const dataChange = (model, value) => {
+  if (model == 'type') {
+    if (value == '3') {
+      infoFields.value.splice(3, 0, { label: '接收人', model: 'user', custom: true });
+      searchUser();
+    } else {
+      infoFields.value.filter((i) => i.model != 'user');
+    }
+  }
+};
+// 查詢用戶
+   const  searchUser=async()=> {
+      // 接收人
+      let user = [];
+      // 企业用户
+      let company = await this.cQuery({ status: '1' });
+      if (company.errcode == '0' && company.total > 0) {
+        for (const val of company.data) {
+          user.push({ id: val.id, company: val.company, phone: val.phone });
+        }
+      }
+      // 科学家用户
+      let scientist = await this.sQuery({ status: '1' });
+      if (scientist.errcode == '0' && scientist.total > 0) {
+        for (const val of scientist.data) {
+          user.push({ id: val.id, name: val.name, phone: val.phone });
+        }
+      }
+      this.$set(this, `userList`, user);
+    },
+    // 获取用户
+    getUser(e) {
+      let user = [];
+      for (const val of e) {
+        let data = this.userList.find((i) => i.id == val);
+        if (data) user.push(data);
+      }
+      return user;
+    },
+// 提交
+const toSave = async (data: { _id: string }) => {
+  let res: IQueryResult;
+  if (data._id) res = await message.update(data);
+  else res = await message.create(data);
+  if (res.errcode == 0) {
+    ElMessage({ type: 'success', message: '维护信息成功' });
+    toBack();
+  } else ElMessage({ type: 'warning', message: `${res.errmsg}` });
+};
+const toBack = () => {
+  window.history.go(-1);
+};
+// 查询其他信息
+const searchOther = async () => {
+  // 字典表---审核状态
+  const p1: IQueryResult = await sysdictdata.query({ dict_type: 'sys_yes_no' });
+  isuseList.value = p1.data as [];
+};
+</script>
+<!-- <script>
+
+  methods: {
+    ...mapActions(['create']),
+    ...sysdictdata({ dQuery: 'query' }),
+    search() {
+      let form = { user_id: this.user.id };
+      this.$set(this, `form`, form);
+    },
+    // 选择接收人类型
+    dataChange({ model, value }) {
+      if (model == 'type') {
+        if (value == '3') {
+          this.fields.splice(3, 0, { label: '接收人', model: 'user', custom: true });
+          this.searchUser();
+        } else {
+          this.fields.filter((i) => i.model != 'user');
+        }
+      }
+    },
+    // 查詢用戶
+    async searchUser() {
+      // 接收人
+      let user = [];
+      // 企业用户
+      let company = await this.cQuery({ status: '1' });
+      if (company.errcode == '0' && company.total > 0) {
+        for (const val of company.data) {
+          user.push({ id: val.id, company: val.company, phone: val.phone });
+        }
+      }
+      // 科学家用户
+      let scientist = await this.sQuery({ status: '1' });
+      if (scientist.errcode == '0' && scientist.total > 0) {
+        for (const val of scientist.data) {
+          user.push({ id: val.id, name: val.name, phone: val.phone });
+        }
+      }
+      this.$set(this, `userList`, user);
+    },
+    // 获取用户
+    getUser(e) {
+      let user = [];
+      for (const val of e) {
+        let data = this.userList.find((i) => i.id == val);
+        if (data) user.push(data);
+      }
+      return user;
+    },
+    // 保存信息
+    async toSave({ data }) {
+      if (data.type == '3') {
+        if (data.user.length > 0) {
+          data.user = this.getUser(data.user);
+          this.createMess(data);
+        } else {
+          this.$message({ message: `请选择指定用户`, type: 'error' });
+        }
+      } else this.createMess(data);
+    },
+    // 创建信息
+    async createMess(e) {
+      let res = await this.create(e);
+      if (this.$checkRes(res, `维护信息完成`, `${res.errmsg}`)) this.toBack();
+    },
+    // 查询其他信息
+    async searchOther() {
+      let res;
+      // 接收人类型
+      res = await this.dQuery({ dict_type: 'studio_message_type' });
+      if (this.$checkRes(res)) {
+        this.$set(this, `typeList`, res.data);
+      }
+    },
+    // 返回
+    toBack() {
+      window.history.go('-1');
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script> -->
+
+<style lang="less" scoped></style>

+ 192 - 0
src/views/center/other/message/index.vue

@@ -0,0 +1,192 @@
+<template>
+  <div id="index">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight">
+        <el-col :span="24" class="one">
+          <component :is="partsSearch" :is_search="true" :fields="fields" @search="partSearch">
+            <template #type>
+              <el-option v-for="i in typeList" :key="i.model" :label="i.dict_label" :value="i.dict_value"></el-option>
+            </template>
+          </component>
+        </el-col>
+        <el-col :span="24" class="two">
+          <component :is="btn" @toAdd="toAdd"></component>
+        </el-col>
+        <el-col :span="24" class="thr">
+          <component
+            :is="CTable"
+            :fields="fields"
+            :opera="opera"
+            :select="false"
+            :selected="selected"
+            @handleSelect="handleSelect"
+            @query="search"
+            :data="tableData"
+            :total="total"
+            @see="toSee"
+            @del="toDel"
+          >
+          </component>
+        </el-col>
+      </el-col>
+    </el-row>
+    <component :is="CDialog" :dialog="dialog" @handleClose="handleClose">
+      <template v-slot:info>
+        <el-col :span="24" class="dialog_one" v-if="dialog.type == '1'">
+          <el-input placeholder="请输入内容" v-model="search_name" class="input-with-select" size="small">
+            <el-button icon="el-icon-search" @click="toSubmit"></el-button>
+          </el-input>
+          <el-col :span="24" class="list">
+            <span v-for="(item, index) in userList" :key="index" class="direction">
+              <span>{{ index + 1 }}.</span>{{ item.name }}
+            </span>
+          </el-col>
+        </el-col>
+      </template>
+    </component>
+  </div>
+</template>
+<script setup lang="ts">
+// #region 组件
+import partsSearch from '@/components/c-search.vue';
+import CTable from '@/components/c-table.vue';
+import btn from '@/components/btn-1.vue';
+import CDialog from '@/components/c-dialog.vue';
+// #endregion
+import type { Ref } from 'vue';
+import { ref, onMounted, getCurrentInstance } from 'vue';
+
+import { ElMessage } from 'element-plus';
+import { useRouter } from 'vue-router';
+// #region 接口
+import { MessageStore } from '@common/src/stores/studio/other/message'; // 列表 // 列表
+import { DictDataStore } from '@common/src/stores/users/sysdictdata'; // 字典表
+import type { IQueryResult } from '@/util/types.util';
+const message = MessageStore();
+const sysdictdata = DictDataStore();
+const { proxy } = getCurrentInstance() as any;
+const router = useRouter();
+// #endregion
+// 列表数据
+let tableData: Ref<any[]> = ref([]);
+// 列表
+let fields: Ref<any[]> = ref([
+  { label: '序号', options: { type: 'index' } },
+  { label: '标题', model: 'title', isSearch: true },
+  { label: '发布时间', model: 'send_time' },
+  {
+    label: '接收人类型',
+    model: 'type',
+    type: 'select',
+    format: (i) => {
+      let data = typeList.value.find((r) => r.dict_value == i);
+      if (data) return data.dict_label;
+    },
+    isSearch: true,
+  },
+]);
+// 操作
+let opera: Ref<any[]> = ref([
+  { label: '查看用户', method: 'see', type: 'success' },
+  { label: '删除', method: 'del', confirm: true, type: 'danger' },
+]);
+// 多选
+let selected: Ref<any[]> = ref([]);
+// 总数
+let total: Ref<number> = ref(0);
+let skip = 0;
+let limit: number = proxy.$limit;
+// 查询数据
+let searchForm: Ref<{}> = ref({});
+// 状态
+let typeList: Ref<any[]> = ref([]);
+let userList: Ref<any[]> = ref([]);
+let users: Ref<any[]> = ref([]);
+let search_name: Ref<String> = ref('');
+// 弹框
+const dialog: Ref<{ type: string; show: boolean; title: string }> = ref({ type: '1', show: false, title: '信息管理' });
+onMounted(async () => {
+  await searchOther();
+  await search({ skip, limit });
+});
+// 查询
+const search = async (e: { skip: number; limit: number }) => {
+  const { skip, limit } = e;
+  let info = { limit: limit, skip: skip, ...searchForm.value, is_use: 'Y' };
+  const res: IQueryResult = await message.query(info);
+  tableData.value = res.data as any[];
+  total.value = res.total;
+};
+// 查询
+const partSearch = (form: { [x: string]: any }) => {
+  searchForm.value = form;
+  search({ skip, limit });
+};
+// 添加
+const toAdd = () => {
+  router.push({ path: '/center/other/message/add' });
+};
+// 搜索用户
+const toSubmit = () => {
+  if (search_name.value) {
+    let info = users.value.filter((f) => f.name.includes(search_name));
+    userList.value = info;
+  } else userList = users;
+};
+// 修改
+const toSee = (data) => {
+  for (const p1 of data.user) {
+    if (!p1.name) p1.name = p1.company;
+  }
+  userList.value = data.user;
+  users.value = data.user;
+  dialog.value = { title: '查看用户', show: true, type: '1' };
+};
+
+// 删除
+const toDel = async (data: { _id: string }) => {
+  const res: IQueryResult = await message.del(data._id);
+  if (res.errcode == 0) {
+    ElMessage({ type: 'success', message: '删除成功' });
+    search({ skip, limit });
+  }
+};
+// 关闭弹窗
+const handleClose = () => {
+  search({ skip, limit });
+  dialog.value = { title: '信息管理', show: false, type: '' };
+};
+
+// 选择
+const handleSelect = () => {};
+// 查询其他信息
+const searchOther = async () => {
+  // 字典表---接收人类型
+  const p1: IQueryResult = await sysdictdata.query({ dict_type: 'studio_message_type' });
+  typeList.value = p1.data as [];
+};
+</script>
+
+<style lang="scss" scoped>
+.two {
+  margin: 0 0 10px 0;
+}
+.direction {
+  display: inline-block;
+  background-color: #409eff;
+  border-radius: 5px;
+  padding: 0 5px;
+  margin: 0 5px 5px 0;
+  color: #ffffff;
+  line-height: 2.5;
+  span {
+    padding: 0 5px 0 0;
+  }
+}
+.dialog_one {
+  padding: 0 15px;
+  .list {
+    margin: 10px 0;
+  }
+}
+</style>

+ 99 - 0
src/views/center/other/notice/add.vue

@@ -0,0 +1,99 @@
+<template>
+  <div id="add">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight">
+        <el-col :span="24" class="one">
+          <component :is="partsSearch" :is_back="true" @toBack="toBack()"></component>
+        </el-col>
+        <el-col :span="24" class="two">
+          <component :is="CForm" :fields="infoFields" :rules="rules" :form="form" labelWidth="auto" @save="toSave">
+            <template #is_use>
+              <el-option v-for="i in isuseList" :key="i.model" :label="i.dict_label" :value="i.dict_value"></el-option>
+            </template>
+            <template #file>
+              <component :is="CUpload" model="file" :limit="limit" :url="url" :list="form.file" @change="onChange"></component>
+            </template>
+            <template #content>
+              <component :is="WangEditor" v-model="form.content" url="/files/studioadmin/other/upload"></component>
+            </template>
+          </component>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+<script setup lang="ts">
+import partsSearch from '@/components/c-search.vue';
+import CForm from '@/components/c-form.vue';
+import WangEditor from '@/components/wang-editor.vue';
+import CUpload from '@/components/c-upload.vue';
+import { useRoute } from 'vue-router';
+import type { Ref } from 'vue';
+import { ref, onMounted, reactive } from 'vue';
+import type { FormRules } from 'element-plus';
+import { ElMessage } from 'element-plus';
+import { NoticeStore } from '@common/src/stores/studio/other/notice'; // 列表 // 列表
+import { DictDataStore } from '@common/src/stores/users/sysdictdata'; // 字典表
+import type { IQueryResult } from '@/util/types.util';
+const notice = NoticeStore();
+const sysdictdata = DictDataStore();
+interface dataItem {}
+let route = useRoute();
+// 表单
+let form: Ref<{ content: string; file: dataItem[] }> = ref({ content: '', file: [] });
+// 必填项
+const rules = reactive<FormRules>({
+  title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
+  date: [{ required: true, message: '请选择发布时间', trigger: 'change' }],
+  origin: [{ required: true, message: '请输入信息来源', trigger: 'change' }],
+});
+// 表单
+let infoFields: Ref<any[]> = ref([
+  { label: '标题', model: 'title' },
+  { label: '发布时间', model: 'date', type: 'date' },
+  { label: '信息来源', model: 'origin' },
+  { label: '信息文件', model: 'file', custom: true },
+  { label: '信息内容', model: 'content', custom: true },
+  { label: '是否启用', model: 'is_use', type: 'select' },
+]);
+let isuseList: Ref<any[]> = ref([]);
+
+let limit: Ref<number> = ref(1);
+let url: Ref<string> = ref('/files/studioadmin/other/upload');
+onMounted(async () => {
+  await searchOther();
+  await search();
+});
+const search = async () => {
+  if (route.query.id) {
+    let id = route.query.id;
+    const res: IQueryResult = await notice.fetch(id);
+    form.value = res.data as { content: string; file: dataItem[] };
+  }
+};
+const onChange = (e: { model: string; value: Array<dataItem> }) => {
+  const { model, value } = e;
+  form.value[model] = value;
+};
+// 提交
+const toSave = async (data: { _id: string }) => {
+  let res: IQueryResult;
+  if (data._id) res = await notice.update(data);
+  else res = await notice.create(data);
+  if (res.errcode == 0) {
+    ElMessage({ type: 'success', message: '维护信息成功' });
+    toBack();
+  } else ElMessage({ type: 'warning', message: `${res.errmsg}` });
+};
+const toBack = () => {
+  window.history.go(-1);
+};
+// 查询其他信息
+const searchOther = async () => {
+  // 字典表---审核状态
+  const p1: IQueryResult = await sysdictdata.query({ dict_type: 'sys_yes_no' });
+  isuseList.value = p1.data as [];
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 140 - 0
src/views/center/other/notice/index.vue

@@ -0,0 +1,140 @@
+<template>
+  <div id="index">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight">
+        <el-col :span="24" class="one">
+          <component :is="partsSearch" :is_search="true" :fields="fields" @search="partSearch">
+            <template #is_use>
+              <el-option v-for="i in isuseList" :key="i.model" :label="i.dict_label" :value="i.dict_value"></el-option>
+            </template>
+          </component>
+        </el-col>
+        <el-col :span="24" class="two">
+          <component :is="btn" @toAdd="toAdd"></component>
+        </el-col>
+        <el-col :span="24" class="thr">
+          <component
+            :is="CTable"
+            :fields="fields"
+            :opera="opera"
+            :select="false"
+            :selected="selected"
+            @handleSelect="handleSelect"
+            @query="search"
+            :data="tableData"
+            :total="total"
+            @edit="toEdit"
+            @del="toDel"
+          >
+          </component>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+<script setup lang="ts">
+// #region 组件
+import partsSearch from '@/components/c-search.vue';
+import CTable from '@/components/c-table.vue';
+import btn from '@/components/btn-1.vue';
+// #endregion
+import type { Ref } from 'vue';
+import { ref, onMounted, getCurrentInstance } from 'vue';
+
+import { ElMessage } from 'element-plus';
+import { useRouter } from 'vue-router';
+// #region 接口
+import { NoticeStore } from '@common/src/stores/studio/other/notice'; // 列表 // 列表
+import { DictDataStore } from '@common/src/stores/users/sysdictdata'; // 字典表
+import type { IQueryResult } from '@/util/types.util';
+const notice = NoticeStore();
+const sysdictdata = DictDataStore();
+const { proxy } = getCurrentInstance() as any;
+const router = useRouter();
+// #endregion
+// 列表数据
+let tableData: Ref<any[]> = ref([]);
+// 列表
+let fields: Ref<any[]> = ref([
+  { label: '序号', options: { type: 'index' } },
+  { label: '标题', model: 'title', isSearch: true },
+  { label: '发布时间', model: 'date' },
+  { label: '信息来源', model: 'origin', isSearch: true },
+  {
+    label: '是否启用',
+    model: 'is_use',
+    type: 'select',
+    format: (i) => {
+      let data = isuseList.value.find((r) => r.dict_value == i);
+      if (data) return data.dict_label;
+    },
+    isSearch: true,
+  },
+]);
+// 操作
+let opera: Ref<any[]> = ref([
+  { label: '修改', method: 'edit' },
+  { label: '删除', method: 'del', confirm: true, type: 'danger' },
+]);
+// 多选
+let selected: Ref<any[]> = ref([]);
+// 总数
+let total: Ref<number> = ref(0);
+let skip = 0;
+let limit: number = proxy.$limit;
+// 查询数据
+let searchForm: Ref<{}> = ref({});
+// 状态
+let isuseList: Ref<any[]> = ref([]);
+onMounted(async () => {
+  await searchOther();
+  await search({ skip, limit });
+});
+// 查询
+const search = async (e: { skip: number; limit: number }) => {
+  const { skip, limit } = e;
+  let info = { limit: limit, skip: skip, ...searchForm.value, is_use: 'Y' };
+  const res: IQueryResult = await notice.query(info);
+  tableData.value = res.data as any[];
+  total.value = res.total;
+};
+// 查询
+const partSearch = (form: { [x: string]: any }) => {
+  searchForm.value = form;
+  search({ skip, limit });
+};
+// 添加
+const toAdd = () => {
+  router.push({ path: '/center/other/notice/add' });
+};
+// 修改
+const toEdit = (data) => {
+  router.push({ path: '/center/other/notice/add', query: { id: data._id } });
+};
+
+// 删除
+const toDel = async (data: { _id: string }) => {
+  const res: IQueryResult = await notice.del(data._id);
+  if (res.errcode == 0) {
+    ElMessage({ type: 'success', message: '删除成功' });
+    search({ skip, limit });
+  }
+};
+
+// 选择
+const handleSelect = () => {};
+// 查询其他信息
+const searchOther = async () => {
+  // 字典表---审核状态
+  const p1: IQueryResult = await sysdictdata.query({ dict_type: 'sys_yes_no' });
+  isuseList.value = p1.data as [];
+};
+</script>
+<style lang="scss" scoped>
+.one {
+  margin: 0 0 10px 0;
+}
+.two {
+  margin: 0 0 10px 0;
+}
+</style>