Browse Source

修改搜索

zs 1 year ago
parent
commit
ecf0c6fd2f

+ 3 - 5
src/App.vue

@@ -1,13 +1,11 @@
 <script setup>
-import { useAppStore } from '@/store/modules/app'
-
-const appStore = useAppStore()
+import zhCN from 'ant-design-vue/es/locale/zh_CN'
 </script>
 
 <template>
-  <el-config-provider :locale="appStore.locale" :size="appStore.size">
+  <a-config-provider :locale="zhCN">
     <router-view />
-  </el-config-provider>
+  </a-config-provider>
 </template>
 
 <style lang="scss">

+ 0 - 92
src/components/Breadcrumb/index.vue

@@ -1,92 +0,0 @@
-<template>
-  <el-breadcrumb class="h-[50px] flex items-center">
-    <transition-group name="breadcrumb-transition">
-      <el-breadcrumb-item v-for="(item, index) in breadcrumbs" :key="item.path">
-        <span v-if="item.redirect === 'noredirect' || index === breadcrumbs.length - 1" class="text-[var(--el-disabled-text-color)]">
-          {{ translateRouteTitle(item.meta.title) }}
-        </span>
-        <a v-else @click.prevent="handleLink(item)">
-          {{ translateRouteTitle(item.meta.title) }}
-        </a>
-      </el-breadcrumb-item>
-    </transition-group>
-  </el-breadcrumb>
-</template>
-
-<script setup>
-import { onBeforeMount, ref, watch } from 'vue'
-import { useRoute } from 'vue-router'
-import { compile } from 'path-to-regexp'
-import { translateRouteTitle } from '@/utils/i18n'
-import router from '@/router'
-
-const currentRoute = useRoute()
-const pathCompile = (path) => {
-  const { params } = currentRoute
-  const toPath = compile(path)
-  return toPath(params)
-}
-
-const breadcrumbs = ref([])
-
-function getBreadcrumb() {
-  let matched = currentRoute.matched.filter((item) => item.meta && item.meta.title)
-  const first = matched[0]
-  if (!isDashboard(first)) {
-    matched = [{ path: '/', meta: { title: 'home' } }].concat(matched)
-  }
-  breadcrumbs.value = matched.filter((item) => {
-    return item.meta && item.meta.title && item.meta.breadcrumb !== false
-  })
-}
-
-function isDashboard(route) {
-  const name = route && route.name
-  if (!name) {
-    return false
-  }
-  return name.toString().trim().toLocaleLowerCase() === 'home'.toLocaleLowerCase()
-}
-
-function handleLink(item) {
-  const { redirect, path, meta } = item
-  if (meta.type == '0') return
-  if (redirect) {
-    router.push(redirect).catch((err) => {
-      console.warn(err)
-    })
-    return
-  }
-  router.push(pathCompile(path)).catch((err) => {
-    console.warn(err)
-  })
-}
-
-watch(
-  () => currentRoute.path,
-  (path) => {
-    if (path.startsWith('/redirect/')) {
-      return
-    }
-    getBreadcrumb()
-  }
-)
-
-onBeforeMount(() => {
-  getBreadcrumb()
-})
-</script>
-<style lang="scss" scoped>
-.app-breadcrumb.el-breadcrumb {
-  display: inline-block;
-  margin-left: 8px;
-  font-size: 14px;
-  line-height: 50px;
-}
-
-// 覆盖 element-plus 的样式
-.el-breadcrumb__inner,
-.el-breadcrumb__inner a {
-  font-weight: 400 !important;
-}
-</style>

+ 0 - 32
src/components/LangSelect/index.vue

@@ -1,32 +0,0 @@
-<script setup>
-// 组件
-import { useI18n } from 'vue-i18n'
-import { useAppStore } from '@/store/modules/app'
-
-const appStore = useAppStore()
-const { locale } = useI18n()
-
-function handleLanguageChange(lang) {
-  locale.value = lang
-  appStore.changeLanguage(lang)
-  if (lang === 'en-us') {
-    ElMessage.success('Switch Language Successful!')
-  } else {
-    ElMessage.success('切换语言成功!')
-  }
-}
-</script>
-
-<template>
-  <el-dropdown trigger="click" @command="handleLanguageChange">
-    <div>
-      <SvgIcon icon-class="language"></SvgIcon>
-    </div>
-    <template #dropdown>
-      <el-dropdown-menu>
-        <el-dropdown-item :disabled="appStore.language === 'zh-cn'" command="zh-cn">中文</el-dropdown-item>
-        <el-dropdown-item :disabled="appStore.language === 'en-us'" command="en-us"> English</el-dropdown-item>
-      </el-dropdown-menu>
-    </template>
-  </el-dropdown>
-</template>

+ 0 - 43
src/components/SvgIcon/index.vue

@@ -1,43 +0,0 @@
-<template>
-  <svg aria-hidden="true" class="svg-icon" :style="'width:' + size + ';height:' + size">
-    <use :xlink:href="symbolId" :fill="color" />
-  </svg>
-</template>
-
-<script setup>
-const props = defineProps({
-  prefix: {
-    type: String,
-    default: 'icon'
-  },
-  iconClass: {
-    type: String,
-    required: false,
-    default: ''
-  },
-  color: {
-    type: String,
-    default: ''
-  },
-  size: {
-    type: String,
-    default: '1em'
-  }
-})
-
-const symbolId = computed(() => `#${props.prefix}-${props.iconClass}`)
-</script>
-
-<style scoped>
-.svg-icon {
-  display: inline-block;
-  width: 1em;
-  height: 1em;
-  overflow: hidden;
-  vertical-align: -0.15em;
-  /* 因icon大小被设置为和字体大小一致,而span等标签的下边缘会和字体的基线对齐,故需设置一个往下的偏移比例,来纠正视觉上的未对齐效果 */
-  outline: none;
-  fill: currentcolor;
-  /* 定义元素的颜色,currentColor是一个变量,这个变量的值就表示当前元素的color值,如果当前元素未设置color值,则从父元素继承 */
-}
-</style>

+ 0 - 40
src/components/custom/custom-button-bar.vue

@@ -1,40 +0,0 @@
-<template>
-  <el-row style="padding: 5px">
-    <el-col :span="24" style="text-align: right">
-      <template v-for="b in fields" :key="b.method">
-        <el-button v-method="b.method" :type="getType(b)" @click="toClick(b)" :disabled="getDisabled(b)">{{ b.label }}</el-button>
-      </template>
-    </el-col>
-  </el-row>
-</template>
-
-<script setup>
-import { get, isFunction } from 'lodash-es'
-const { t } = useI18n()
-const props = defineProps({
-  fields: { type: Array, default: () => [] }
-})
-const emits = defineEmits([])
-const getType = (field) => {
-  return get(field, 'type', 'primary')
-}
-const toClick = (field) => {
-  const method = get(field, 'method')
-  if (!method) {
-    ElMessage({ type: 'error', message: t('common.no_method') })
-    return
-  }
-  emits(method)
-}
-const getDisabled = (field) => {
-  const disabled = get(field, 'disabled')
-  if (!disabled) return false
-  if (isFunction(disabled)) return disabled(field)
-  return disabled
-}
-</script>
-<style scoped>
-.el-button {
-  margin-top: 5px;
-}
-</style>

+ 0 - 193
src/components/custom/custom-form.vue

@@ -1,193 +0,0 @@
-<template>
-  <div id="custom-form">
-    <el-form ref="formRef" :model="form" :rules="rules" :label-width="labelWidth" class="form" @submit.prevent :disabled="disabled">
-      <el-col :span="span" v-for="(item, index) in fields" :key="index">
-        <el-form-item v-if="display(item)" :key="`form-field-${item.model}`" :label="getField('label', item)" :prop="item.model" :required="item.required">
-          <template v-if="item.custom">
-            <slot :name="item.model" v-bind="{ item }"></slot>
-          </template>
-          <template v-else>
-            <template v-if="item.type === 'textarea'">
-              <el-input
-                clearable
-                v-model="form[item.model]"
-                :type="item.type"
-                :placeholder="getField('placeholder', item)"
-                v-bind="item.options"
-                @change="dataChange(item.model)"
-                show-word-limit
-              ></el-input>
-            </template>
-            <template v-else-if="item.type === 'numbers'">
-              <el-input-number v-model="form[item.model]" :placeholder="getField('placeholder', item)" @change="dataChange(item.model)" style="width: 100%" />
-            </template>
-            <template v-else-if="item.type === 'radio'">
-              <el-radio-group v-model="form[item.model]" :type="item.type" v-bind="item.options" @change="dataChange(item.model)">
-                <slot :name="item.model" v-bind="{ item }"></slot>
-              </el-radio-group>
-            </template>
-            <template v-else-if="item.type === 'checkbox'">
-              <el-checkbox-group v-model="form[item.model]" :type="item.type" v-bind="item.options">
-                <slot :name="item.model" v-bind="{ item }"></slot>
-              </el-checkbox-group>
-            </template>
-            <template v-else-if="item.type === 'select'">
-              <el-select
-                clearable
-                filterable
-                allow-create
-                default-first-option
-                v-model="form[item.model]"
-                :type="item.type"
-                :placeholder="getField('selectplaceholder', item)"
-                v-bind="item.options"
-                @change="dataChange(item.model)"
-                style="width: 100%"
-              >
-                <slot :name="item.model" v-bind="{ item }"></slot>
-              </el-select>
-            </template>
-            <template v-else-if="item.type === 'selectMany'">
-              <el-select
-                filterable
-                clearable
-                multiple
-                collapse-tags
-                v-model="form[item.model]"
-                :type="item.type"
-                :placeholder="getField('selectplaceholder', item)"
-                v-bind="item.options"
-                @change="dataChange(item.model)"
-                style="width: 100%"
-              >
-                <slot :name="item.model" v-bind="{ item }"></slot>
-              </el-select>
-            </template>
-            <template v-else-if="item.type === `year` || item.type == 'month' || item.type == 'date' || item.type == 'daterange' || item.type == 'datetime' || item.type == 'datetimerange'">
-              <el-date-picker
-                v-model="form[item.model]"
-                :type="item.type"
-                :placeholder="getField('selectplaceholder', item)"
-                :format="getDateFormat(item.type)"
-                :value-format="getDateFormat(item.type)"
-                v-bind="item.options"
-                @change="dataChange(item.model)"
-                range-separator="至"
-                style="width: 100%"
-              >
-              </el-date-picker>
-            </template>
-            <template v-else-if="item.type === `time`">
-              <el-time-picker
-                v-model="form[item.model]"
-                :placeholder="getField('selectplaceholder', item)"
-                :format="getDateFormat(item.type)"
-                :value-format="getDateFormat(item.type)"
-                v-bind="item.options"
-                @change="dataChange(item.model)"
-                style="width: 100%"
-              >
-              </el-time-picker>
-            </template>
-            <template v-else-if="item.type === `inputnumber`">
-              <el-input-number v-model="form[item.model]" :placeholder="getField('placeholder', item)" v-bind="item.options" @change="dataChange(item.model)" style="width: 100%"></el-input-number>
-            </template>
-            <template v-else>
-              <el-input
-                clearable
-                v-model="form[item.model]"
-                :type="getField('type', item)"
-                :placeholder="getField('placeholder', item)"
-                :show-password="getField('type', item) === 'password'"
-                v-bind="item.options"
-                @change="dataChange(item.model)"
-              ></el-input>
-            </template>
-          </template>
-        </el-form-item>
-      </el-col>
-      <el-col :span="24" label="" class="btn" v-if="useSave">
-        <slot name="submit">
-          <el-button type="primary" @click="save(formRef)">{{ submitText || submitTextDefault }}</el-button>
-        </slot>
-      </el-col>
-    </el-form>
-  </div>
-</template>
-
-<script setup>
-import { get, isFunction } from 'lodash-es'
-const { t } = useI18n()
-const submitTextDefault = t('common.save')
-const props = defineProps({
-  modelValue: { type: Object },
-  rules: { type: Array, default: () => {} },
-  labelWidth: { type: String, default: 'auto' },
-  disabled: { type: Boolean, default: false },
-  fields: { type: Array, default: () => [] },
-  submitText: { type: String },
-  useSave: { type: Boolean, default: true },
-  span: { type: Number, default: 24 } // 限制两侧的距离,24就是整行全用
-})
-const emits = defineEmits(['update:modelValue', 'dataChange', 'save'])
-const formRef = ref()
-const form = computed({
-  get() {
-    return props.modelValue
-  },
-  set(value) {
-    console.log(value)
-    emits('update:modelValue', value)
-  }
-})
-const save = async (formEl) => {
-  if (!formEl) return
-  await formEl.validate((valid, fields) => {
-    if (valid) {
-      emits('save', form.value)
-    } else {
-      console.log('error submit!', fields)
-    }
-  })
-}
-const getField = (item, data) => {
-  let res = get(data, item, null)
-  if (item === 'type') res = res === null ? `text` : res
-  if (item === 'placeholder') res = res === null ? `请输入${data.label}` : res
-  if (item === `selectplaceholder`) res = res === null ? `请选择${data.label}` : res
-  if (item === 'required') res = res === null ? false : res
-  if (item === `error`) res = res === null ? `${data.label}错误` : res
-  return res
-}
-const dataChange = (model) => {
-  const value = form.value[model]
-  emits('dataChange', { model, value })
-}
-const display = (field) => {
-  let dis = get(field, `display`)
-  if (!isFunction(dis)) return true
-  else {
-    return dis(field, form)
-  }
-}
-const getDateFormat = (e) => {
-  if (e === 'year') return 'YYYY'
-  if (e === 'month') return 'MM'
-  if (e === 'date') return 'YYYY-MM-DD'
-  if (e === 'daterange') return 'YYYY-MM-DD'
-  if (e === 'datetime') return 'YYYY-MM-DD HH:mm:ss'
-  if (e === 'datetimerange') return 'YYYY-MM-DD HH:mm:ss'
-  if (e === 'time') return 'HH:mm:ss'
-}
-</script>
-<style scoped>
-.btn {
-  text-align: center;
-}
-
-.form {
-  display: flex;
-  flex-direction: row;
-  flex-wrap: wrap;
-}
-</style>

+ 0 - 150
src/components/custom/custom-search-bar.vue

@@ -1,150 +0,0 @@
-<template>
-  <div id="custom-search-bar">
-    <el-form ref="formRef" :model="form" :label-width="labelWidth" class="form" @submit.prevent :inline="true">
-      <el-form-item v-for="item in fields" :key="`form-field-${item.model}`" :label="getField('label', item)" :prop="item.model" :required="item.required">
-        <template v-if="item.custom">
-          <slot :name="item.model" v-bind="{ item }"></slot>
-        </template>
-        <template v-else>
-          <template v-if="item.type === 'numbers'">
-            <el-input-number v-model="form[item.model]" :placeholder="getField('placeholder', item)" @change="dataChange(item.model)" style="width: 100%" />
-          </template>
-          <template v-else-if="item.type === 'radio'">
-            <el-radio-group v-model="form[item.model]" :type="item.type" v-bind="item.options" @change="dataChange(item.model)">
-              <slot :name="item.model" v-bind="{ item }"></slot>
-            </el-radio-group>
-          </template>
-          <template v-else-if="item.type === 'checkbox'">
-            <el-checkbox-group v-model="form[item.model]" :type="item.type" v-bind="item.options">
-              <slot :name="item.model" v-bind="{ item }"></slot>
-            </el-checkbox-group>
-          </template>
-          <template v-else-if="item.type === 'select'">
-            <el-select
-              clearable
-              filterable
-              allow-create
-              default-first-option
-              v-model="form[item.model]"
-              :type="item.type"
-              :placeholder="getField('selectplaceholder', item)"
-              v-bind="item.options"
-              @change="dataChange(item.model)"
-              style="width: 100%; min-width: 200px"
-            >
-              <slot :name="item.model" v-bind="{ item }"></slot>
-            </el-select>
-          </template>
-          <template v-else-if="item.type === 'selectMany'">
-            <el-select
-              filterable
-              clearable
-              multiple
-              collapse-tags
-              v-model="form[item.model]"
-              :type="item.type"
-              :placeholder="getField('selectplaceholder', item)"
-              v-bind="item.options"
-              @change="dataChange(item.model)"
-              style="width: 100%"
-            >
-              <slot :name="item.model" v-bind="{ item }"></slot>
-            </el-select>
-          </template>
-          <template v-else-if="item.type === `year` || item.type == 'month' || item.type == 'date' || item.type == 'daterange' || item.type == 'datetime' || item.type == 'datetimerange'">
-            <el-date-picker
-              v-model="form[item.model]"
-              :type="item.type"
-              :placeholder="getField('selectplaceholder', item)"
-              :format="getDateFormat(item.type)"
-              :value-format="getDateFormat(item.type)"
-              v-bind="item.options"
-              @change="dataChange(item.model)"
-              range-separator="至"
-              style="width: 100%"
-            >
-            </el-date-picker>
-          </template>
-          <template v-else-if="item.type === `time`">
-            <el-time-picker
-              v-model="form[item.model]"
-              :placeholder="getField('selectplaceholder', item)"
-              :format="getDateFormat(item.type)"
-              :value-format="getDateFormat(item.type)"
-              v-bind="item.options"
-              @change="dataChange(item.model)"
-              style="width: 100%"
-            >
-            </el-time-picker>
-          </template>
-          <template v-else-if="item.type === `inputnumber`">
-            <el-input-number v-model="form[item.model]" :placeholder="getField('placeholder', item)" v-bind="item.options" @change="dataChange(item.model)" style="width: 100%"></el-input-number>
-          </template>
-          <template v-else>
-            <el-input
-              clearable
-              v-model="form[item.model]"
-              :type="getField('type', item)"
-              :placeholder="getField('placeholder', item)"
-              :show-password="getField('type', item) === 'password'"
-              v-bind="item.options"
-              @change="dataChange(item.model)"
-            ></el-input>
-          </template>
-        </template>
-      </el-form-item>
-      <el-form-item v-if="fields.length > 0">
-        <el-button type="primary" @click="toClick">{{ $t('common.search') }}</el-button>
-        <el-button type="default" @click="toReset">{{ $t('common.reset') }}</el-button>
-      </el-form-item>
-    </el-form>
-  </div>
-</template>
-
-<script setup>
-import { get } from 'lodash-es'
-const props = defineProps({
-  modelValue: { type: Object },
-  fields: { type: Array, default: () => [] }
-})
-const emits = defineEmits(['update:modelValue', 'dataChange', 'search'])
-const formRef = ref()
-const form = computed({
-  get() {
-    return props.modelValue
-  },
-  set(value) {
-    console.log(value)
-    emits('update:modelValue', value)
-  }
-})
-const toClick = () => {
-  emits('search')
-}
-const toReset = () => {
-  emits('reset')
-}
-const getField = (item, data) => {
-  let res = get(data, item, null)
-  if (item === 'type') res = res === null ? `text` : res
-  if (item === 'placeholder') res = res === null ? `请输入${data.label}` : res
-  if (item === `selectplaceholder`) res = res === null ? `请选择${data.label}` : res
-  if (item === 'required') res = res === null ? false : res
-  if (item === `error`) res = res === null ? `${data.label}错误` : res
-  return res
-}
-const dataChange = (model) => {
-  const value = form.value[model]
-  emits('dataChange', { model, value })
-}
-const getDateFormat = (e) => {
-  if (e === 'year') return 'YYYY'
-  if (e === 'month') return 'MM'
-  if (e === 'date') return 'YYYY-MM-DD'
-  if (e === 'daterange') return 'YYYY-MM-DD'
-  if (e === 'datetime') return 'YYYY-MM-DD HH:mm:ss'
-  if (e === 'datetimerange') return 'YYYY-MM-DD HH:mm:ss'
-  if (e === 'time') return 'HH:mm:ss'
-}
-</script>
-<style scoped></style>

+ 0 - 121
src/components/custom/custom-table.vue

@@ -1,121 +0,0 @@
-<template>
-  <el-row>
-    <el-col>
-      <el-table :data="data" border :height="height" @selection-change="toSelect">
-        <el-table-column type="selection" width="55" v-if="select"> </el-table-column>
-        <template v-for="f in fields" :key="f.model">
-          <el-table-column v-if="f.custom" :label="f.label" :prop="f.model" align="center" v-bind="f.options">
-            <template v-slot="{ row }">
-              <slot :name="f.model" v-bind="{ f, row }"></slot>
-            </template>
-          </el-table-column>
-          <el-table-column v-else :label="f.label" :prop="f.model" align="center" :formatter="toFormatter"></el-table-column>
-        </template>
-        <el-table-column :label="$t('common.opera')" align="center" v-if="opera.length > 0">
-          <template v-slot="{ row, $index }">
-            <slot v-bind="{ row }">
-              <template v-for="f in opera">
-                <template v-if="display(f, row)">
-                  <el-link :key="f.method" :type="f.type || 'primary'" size="small" :underline="false" class="link" v-method="f.method" @click="handleOpera(f, row, $index)">
-                    {{ f.label }}
-                  </el-link>
-                </template>
-              </template>
-            </slot>
-          </template>
-        </el-table-column>
-      </el-table>
-    </el-col>
-  </el-row>
-  <el-row justify="end">
-    <el-pagination
-      background
-      layout="total, prev, pager, next"
-      :page-sizes="[10, 20, 50, 100, 200]"
-      :total="total"
-      :page-size="limit"
-      v-model:current-page="currentPage"
-      @current-change="changePage"
-      @size-change="sizeChange"
-    >
-    </el-pagination>
-  </el-row>
-</template>
-<script setup>
-import { isFunction, get, isString, cloneDeep } from 'lodash-es'
-const props = defineProps({
-  data: { type: Array, default: () => [] },
-  height: { type: String, default: '60vh' },
-  fields: { type: Array, default: () => [] },
-  opera: { type: Array, default: () => [] },
-  total: { type: Number, default: 0 },
-  limit: { type: Number, default: 10 },
-  select: { type: Boolean, default: false }
-})
-const emit = defineEmits(['query', 'toSelect'])
-const toSelect = (val) => {
-  emit(`toSelect`, val)
-}
-
-const handleOpera = (field, data, index) => {
-  let { method, confirm = false, methodZh, label, confirmWord } = cloneDeep(field)
-  if (isFunction(methodZh)) methodZh = methodZh(data)
-  else if (isString(methodZh)) {
-    methodZh = label
-  }
-  if (confirm) {
-    let word = methodZh ? methodZh : `您确认${label}该数据?`
-    if (confirmWord) word = confirmWord
-    ElMessageBox.confirm(word, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
-      .then(() => {
-        emit(method, cloneDeep(data), index)
-      })
-      .catch(() => {})
-  } else emit(method, cloneDeep(data), index)
-}
-
-/**
- * 根据field中的 format函数 格式化该单元格数据
- * @param {Object} row 本行数据
- * @param {Object} column 本列实例
- * @param {any} cellValue 该单元格原数据
- */
-const toFormatter = (row, column, cellValue) => {
-  // 先找到field
-  const fields = get(props, 'fields')
-  if (!fields) return cellValue
-  let this_field = fields.find((fil) => fil.model === column.property)
-  if (!this_field) return cellValue
-  // 再找field中format函数
-  let format = get(this_field, `format`, false)
-  if (!format) return cellValue
-  if (isFunction(format)) {
-    const formatResult = format(cellValue, row, this_field)
-    return formatResult
-  }
-}
-const display = (field, row) => {
-  let display = get(field, `display`, true)
-  if (display === true) return true
-  else {
-    let res = display(row)
-    return res
-  }
-}
-const currentPage = ref(1)
-// 分页
-const changePage = (page = currentPage.value) => {
-  emit('query', { skip: (page - 1) * props.limit, limit: props.limit })
-}
-</script>
-<style scoped>
-.el-row {
-  padding-top: 20px;
-}
-.link {
-  padding: 0 5px 0 0;
-}
-.page {
-  margin: 10px 0 0 0;
-}
-</style>

+ 0 - 96
src/components/custom/custom-upload.vue

@@ -1,96 +0,0 @@
-<template>
-  <div id="c-upload">
-    <el-upload
-      v-if="url"
-      ref="upload"
-      :action="url"
-      :limit="limit"
-      :accept="accept"
-      :file-list="list"
-      :list-type="listType"
-      :on-exceed="outLimit"
-      :on-preview="filePreview"
-      :on-success="onSuccess"
-      :before-remove="onRemove">
-      <el-button type="primary">选择文件</el-button>
-      <template #tip v-if="tip">
-        <p style="color: #ff0000">{{ tip }}</p>
-      </template>
-    </el-upload>
-    <el-dialog v-model="dialog.show" append-to-body>
-      <img width="100%" :src="dialog.url" alt="" />
-    </el-dialog>
-  </div>
-</template>
-
-<script setup>
-import { ElMessage } from 'element-plus'
-import { omit, cloneDeep, isArray } from 'lodash-es'
-
-let dialog = ref({ show: false, url: '' })
-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' }, //'text' | 'picture' | 'picture-card'
-  tip: { type: String, default: () => undefined },
-  list: { type: Array, default: () => [] },
-  model: { type: String, default: () => '' }
-})
-// 图片上传地址
-const { url } = toRefs(props)
-// 可上传文件数目
-const { limit } = toRefs(props)
-// 接收上传的文件类型
-const { accept } = toRefs(props)
-// 文件列表的类型--picture-card---picture
-const { listType } = toRefs(props)
-// 文件提醒
-const { tip } = toRefs(props)
-// 已有数据,赋值,预览
-const { list } = toRefs(props)
-const { model } = toRefs(props)
-// const list = ref<UploadUserFile[]>([]);
-
-const emit = defineEmits(['change'])
-// 图片预览
-const filePreview = (file) => {
-  // this.dialog = { show: true, url: file.url };
-  window.open(file.url)
-};
-// 只允许上传多少个文件
-const outLimit = () => {
-  ElMessage.error(`只允许上传${limit.value}个文件`)
-};
-// 上传成功,response:成功信息,file:图片信息,fileList:图片列表
-const onSuccess = (response, file) => {
-  if (response.errcode !== 0) {
-    ElMessage({ type: 'error', message: '删除成功' })
-    return
-  }
-  let ponse = omit(response, ['errcode', 'errmsg'])
-  let arr = cloneDeep(list)
-  if (isArray(list.value)) {
-    arr.value.push({ ...ponse, name: file.name, url: `${import.meta.env.VITE_APP_HOST}${response.uri}` })
-  } else {
-    arr.value = [{ ...ponse, name: file.name, url: `${import.meta.env.VITE_APP_HOST}${response.uri}` }]
-  }
-  emit('change', { model: model.value, value: arr.value })
-}
-// 删除图片
-// file: { id: any; uri: string }, fileList: any
-const onRemove = () => {
-  // let arr: Ref<ListItem[]> = _.cloneDeep(list);
-  // let info = arr.value.filter((f) => f.id != file.id);
-  // emit('change', info);
-  return true
-};
-
-// #endregion
-</script>
-
-<style lang="scss" scoped>
-#c-upload {
-  width: 100%;
-}
-</style>

+ 0 - 23
src/lang/index.js

@@ -1,23 +0,0 @@
-import { createI18n } from 'vue-i18n'
-// 本地语言包
-// tm('key') 取ref对象 再用toRaw转换成普通对象
-// t('key.target')取值
-import enLocale from './package/en'
-import zhCnLocale from './package/zh-cn'
-const messages = {
-  'zh-cn': {
-    ...zhCnLocale
-  },
-  'en-us': {
-    ...enLocale
-  }
-}
-
-const i18n = createI18n({
-  legacy: false,
-  locale: localStorage.getItem('language'),
-  messages: messages,
-  globalInjection: true
-})
-
-export default i18n

+ 0 - 8
src/lang/package/en.js

@@ -1,8 +0,0 @@
-const allModules = import.meta.glob('./en/*.js', { eager: true })
-const keys = Object.keys(allModules)
-const packages = {}
-for (const key of keys) {
-  const name = key.substring(key.lastIndexOf('/') + 1, key.lastIndexOf('.js'))
-  packages[name] = allModules[key].default
-}
-export default packages

+ 0 - 19
src/lang/package/en/common.js

@@ -1,19 +0,0 @@
-export default {
-  opera: '操作',
-  add: '添加',
-  update: '修改',
-  delete: '删除',
-  delete_confirm: '您确定删除该数据?',
-  search: '查询',
-  view: '查看',
-  save: '保存',
-  submit: '提交',
-  is_use_abled: '启用',
-  is_use_disabled: '禁用',
-  yes: '是',
-  no: '否',
-  no_method: '功能暂未开放',
-  warning: '注意',
-  confirm: '确定',
-  cancel: '取消'
-}

+ 0 - 19
src/lang/package/en/errorMessage.js

@@ -1,19 +0,0 @@
-export default {
-  UNKNOW: 'unknow',
-  BADPARAM: 'bad param',
-  NETWORK: 'network',
-  JSON_ERROR: 'json error',
-  USER_NOT_EXIST: 'user not exist',
-  BAD_PASSWORD: 'bad password',
-  NOT_LOGIN: 'not login',
-  ACCESS_DENIED: 'access denied',
-  DATA_NOT_EXIST: 'data not exist',
-  DATA_EXISTED: 'data existed',
-  DATA_INVALID: 'data invalid',
-  VERIFYCODE_INVALID: 'verifycode invalid',
-  SERVICE_FAULT: 'service fault',
-  DATABASE_FAULT: 'database fault',
-  FILE_FAULT: 'file fault',
-  USER_NOT_BIND: 'user not bind',
-  BUSINESS: 'business error'
-}

+ 0 - 10
src/lang/package/en/login.js

@@ -1,10 +0,0 @@
-// 登录页面国际化
-export default {
-  title: 'Information Technology Incubation Platform',
-  username: 'Username',
-  password: 'Password',
-  login: 'Login',
-  captchaCode: 'Verify Code',
-  placeholder1: 'please select your username',
-  placeholder2: 'please select your password'
-}

+ 0 - 9
src/lang/package/en/menus.js

@@ -1,9 +0,0 @@
-export default {
-  home: 'home',
-  system: 'system',
-  system_menus: 'system_menus',
-  system_role: 'system_role',
-  system_parameter: 'system_parameter',
-  system_dict: 'system_dict',
-  system_dict_data: 'system_dictData'
-}

+ 0 - 6
src/lang/package/en/navbar.js

@@ -1,6 +0,0 @@
-// 导航栏国际化
-export default {
-  dashboard: 'Dashboard',
-  logout: 'Logout',
-  my: 'Personal Center'
-}

+ 0 - 55
src/lang/package/en/pages.js

@@ -1,55 +0,0 @@
-export default {
-  menus: {
-    dialogTitle: '菜单信息',
-    is_default: '是否默认',
-    name: '菜单名称',
-    namePh: '请填写菜单名称',
-    route_name: '路由名称',
-    route_namePh: '请填写路由名称',
-    i18n_code: '国际化编码',
-    i18n_codePh: '请填写国际化编码并确保与国际化文件中的编码一致',
-    parentName: '父级菜单',
-    icon: '图标',
-    iconPh: '请选择图标',
-    order_num: '顺序',
-    path: '路由地址',
-    pathPh: '请填写路由地址',
-    component: '组件地址',
-    componentPh: '请填写组件地址',
-    type: '菜单类型',
-    typePh: '请选择菜单类型',
-    is_use: '状态',
-    remark: '备注',
-    remarkPh: '请输入备注',
-    addNext: '添加下一级',
-    baseInfo: '基本信息',
-    configInfo: '功能列表',
-    config_zh: '功能说明',
-    config_code: '功能编码',
-    add_config: '添加功能'
-  },
-  role: {
-    dialogTitle: '角色信息',
-    name: '角色名称',
-    namePh: '请填写角色名称',
-    code: '角色代码',
-    codePh: '请填写角色代码',
-    brief: '简介',
-    menu: '权限配置',
-    is_use: '是否启用'
-  },
-  admin: {
-    dialogTitle: '用户信息',
-    account: '账号',
-    nick_name: '名称',
-    role: '角色',
-    is_super: '是否是超级管理员',
-    is_use: '是否启用',
-    bind: '绑定用户',
-    rp: '重置密码',
-    rpConfirm: '您确定要重置密码?',
-    changeToAbled: '您确定要启用该用户?',
-    changeToDisabled: '您确定要禁用该用户?',
-    password: '密码'
-  }
-}

+ 0 - 8
src/lang/package/zh-cn.js

@@ -1,8 +0,0 @@
-const allModules = import.meta.glob('./zh-cn/*.js', { eager: true })
-const keys = Object.keys(allModules)
-const packages = {}
-for (const key of keys) {
-  const name = key.substring(key.lastIndexOf('/') + 1, key.lastIndexOf('.js'))
-  packages[name] = allModules[key].default
-}
-export default packages

+ 0 - 24
src/lang/package/zh-cn/common.js

@@ -1,24 +0,0 @@
-export default {
-  opera: '操作',
-  back: '返回',
-  add: '添加',
-  update: '修改',
-  delete: '删除',
-  exam: '审核',
-  dict: '字典数据',
-  sign: '报名管理',
-  delete_confirm: '您确定删除该数据?',
-  search: '查询',
-  reset: '重置',
-  view: '查看',
-  save: '保存',
-  submit: '提交',
-  is_use_abled: '启用',
-  is_use_disabled: '禁用',
-  yes: '是',
-  no: '否',
-  no_method: '功能暂未开放',
-  warning: '注意',
-  confirm: '确定',
-  cancel: '取消'
-}

+ 0 - 19
src/lang/package/zh-cn/errorMessage.js

@@ -1,19 +0,0 @@
-export default {
-  UNKNOW: '系统错误',
-  BADPARAM: '参数错误',
-  NETWORK: '网络错误',
-  JSON_ERROR: 'JSON错误',
-  USER_NOT_EXIST: '用户不存在',
-  BAD_PASSWORD: '密码错误',
-  NOT_LOGIN: '未登录',
-  ACCESS_DENIED: '禁止访问',
-  DATA_NOT_EXIST: '数据不存在',
-  DATA_EXISTED: '数据已存在',
-  DATA_INVALID: '无效数据',
-  VERIFYCODE_INVALID: '验证码无效',
-  SERVICE_FAULT: '服务错误',
-  DATABASE_FAULT: '数据库错误',
-  FILE_FAULT: '文件错误',
-  USER_NOT_BIND: '用户未绑定',
-  BUSINESS: '业务错误'
-}

+ 0 - 10
src/lang/package/zh-cn/login.js

@@ -1,10 +0,0 @@
-// 登录页面国际化
-export default {
-  title: '新一代信息技术孵化平台',
-  username: '用户名',
-  password: '密码',
-  login: '登 录',
-  captchaCode: '验证码',
-  placeholder1: '请输入用户名',
-  placeholder2: '请输入密码'
-}

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

@@ -1,25 +0,0 @@
-export default {
-  home: '首页',
-  system: '系统设置',
-  user: '用户管理',
-  center: '个人中心',
-  password: '修改密码',
-  system_menus: '菜单设置',
-  system_role: '角色设置',
-  system_parameter: '系统参数',
-  system_dict: '字典管理',
-  system_dict_data: '字典数据',
-  user_admin: '管理员用户',
-  user_user: '平台用户',
-  platform: '信息管理',
-  platform_policy: '政策法规',
-  platform_news: '新闻资讯',
-  platform_demand: '供需管理',
-  platform_match: '赛事管理',
-  platform_achievement: '成果管理',
-  demand_supply: '供给信息管理',
-  demand_demand: '需求信息管理',
-  match: '赛事管理',
-  match_sign: '报名管理',
-  achievement: '成果管理'
-}

+ 0 - 6
src/lang/package/zh-cn/navbar.js

@@ -1,6 +0,0 @@
-// 导航栏国际化
-export default {
-  dashboard: '首页',
-  logout: '注销',
-  my: '个人中心'
-}

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

@@ -1,154 +0,0 @@
-export default {
-  menus: {
-    dialogTitle: '菜单信息',
-    is_default: '是否默认',
-    name: '菜单名称',
-    namePh: '请填写菜单名称',
-    route_name: '路由名称',
-    route_namePh: '请填写路由名称',
-    i18n_code: '国际化编码',
-    i18n_codePh: '请填写国际化编码并确保与国际化文件中的编码一致',
-    parentName: '父级菜单',
-    icon: '图标',
-    iconPh: '请选择图标',
-    order_num: '顺序',
-    path: '路由地址',
-    pathPh: '请填写路由地址',
-    component: '组件地址',
-    componentPh: '请填写组件地址',
-    type: '菜单类型',
-    typePh: '请选择菜单类型',
-    is_use: '状态',
-    remark: '备注',
-    remarkPh: '请输入备注',
-    addNext: '添加下一级',
-    baseInfo: '基本信息',
-    configInfo: '功能列表',
-    config_zh: '功能说明',
-    config_code: '功能编码',
-    add_config: '添加功能'
-  },
-  role: {
-    dialogTitle: '角色信息',
-    name: '角色名称',
-    namePh: '请填写角色名称',
-    code: '角色代码',
-    codePh: '请填写角色代码',
-    brief: '简介',
-    menu: '权限配置',
-    is_use: '是否启用'
-  },
-  admin: {
-    dialogTitle: '用户信息',
-    account: '账号',
-    nick_name: '名称',
-    role: '角色',
-    is_super: '是否是超级管理员',
-    is_use: '是否启用',
-    bind: '绑定用户',
-    rp: '重置密码',
-    rpConfirm: '您确定要重置密码?',
-    changeToAbled: '您确定要启用该用户?',
-    changeToDisabled: '您确定要禁用该用户?',
-    password: '密码'
-  },
-  dict: {
-    addDialogTitle: '新增字典类型',
-    upDialogTitle: '修改字典类型',
-    title: '字典名称',
-    code: '编码',
-    is_use: '是否启用',
-    remark: '备注',
-    titleMessage: '请输入字典名称',
-    codeMessage: '请输入编码'
-  },
-  dictData: {
-    dialogTitle: '管理字典数据',
-    codeDialogTitle: '字典数据',
-    label: '数据显示值',
-    value: '数据选择值',
-    sort: '排序',
-    is_use: '是否启用',
-    labelMessage: '请输入数据显示值',
-    valueMessage: '请输入数据选择值'
-  },
-  news: {
-    addDialogTitle: '新增新闻',
-    upDialogTitle: '修改新闻',
-    examDialogTitle: '审核新闻',
-    title: '标题',
-    person: '发布人',
-    time: '发布时间',
-    number: '浏览次数',
-    content: '内容',
-    is_use: '是否启用',
-    status: '审核状态',
-    titleMessage: '请输入标题'
-  },
-  demand: {
-    addDialogTitle: '新增需求',
-    upDialogTitle: '修改需求',
-    examDialogTitle: '审核需求',
-    name: '需求名称',
-    field: '行业领域',
-    urgent: '需求紧急度',
-    method: '合作方式',
-    time: '有效期',
-    money: '价格(万元)',
-    area: '需求地区',
-    brief: '简介',
-    status: '审核状态',
-    is_use: '是否启用',
-    demand_status: '需求状态',
-    titleMessage: '请输入需求名称'
-  },
-  match: {
-    addDialogTitle: '新增需赛事',
-    upDialogTitle: '修改赛事',
-    examDialogTitle: '审核赛事',
-    name: '赛事名称',
-    type: '赛事类型',
-    file: '封面',
-    time: '有效期',
-    money: '奖金(万元)',
-    rules: '赛事规则',
-    brief: '常见问题',
-    status: '审核状态',
-    match_status: '赛事状态',
-    is_use: '是否启用',
-    titleMessage: '请输入需求名称',
-    rules1: '大赛背景',
-    rules2: '大赛主题和目标',
-    rules3: '大赛基本情况介绍',
-    rules4: '赛题任务',
-    rules5: '赛制阶段',
-    rules6: '参赛资格',
-    rules7: '参赛报名',
-    rules8: '奖项设置与奖励办法',
-    rules9: '组织单位',
-    rules10: '赛事交流',
-    rules11: '赛事联络'
-  },
-  achievement: {
-    addDialogTitle: '新增成果',
-    upDialogTitle: '修改成果',
-    examDialogTitle: '审核成果',
-    name: '成果名称',
-    patent: '专利号',
-    field: '行业领域',
-    type: '类型',
-    attribute: '属性',
-    mature: '成熟度',
-    sell: '出让方式',
-    technology: '技术分类',
-    time: '发布时间',
-    money: '价格(万元)',
-    area: '成果地区',
-    brief: '简介',
-    file: '附件',
-    status: '审核状态',
-    is_use: '是否启用',
-    achievement_status: '成果状态',
-    titleMessage: '请输入需求成果名称'
-  }
-}

+ 1 - 1
src/layout/index.vue

@@ -256,7 +256,7 @@ provide('selectMenu', selectMenu)
     }
   }
   .center {
-    min-height: 77.3vh;
+    min-height: 77vh;
   }
   .bottom {
     width: 100%;

+ 7 - 8
src/main.js

@@ -4,19 +4,17 @@ import { setupStore } from '@/store'
 import App from './App.vue'
 import router from './router'
 
+// element
+import ElementPlus from 'element-plus'
+import 'element-plus/dist/index.css'
+import locale from 'element-plus/es/locale/lang/zh-cn'
 import * as ElementPlusIconsVue from '@element-plus/icons-vue'
 
-// 本地SVG图标
-import 'virtual:svg-icons-register'
-// 国际化
-import i18n from '@/lang/index'
 // 请求检查函数
 import { InitCheckResult } from './utils/checkResult'
 import { InitVariable } from './utils/variable'
 // 组件
 import globalComponents from '@/components'
-// 指令
-import { InitDirective } from './utils/directives'
 // 自动滚动
 import vue3SeamlessScroll from 'vue3-seamless-scroll'
 // Antd
@@ -29,9 +27,10 @@ setupStore(app)
 for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
   app.component(key, component)
 }
-app.use(i18n).use(router).mount('#app')
+app.use(router)
 app.use(Antd)
+app.use(ElementPlus, { locale })
 app.use(vue3SeamlessScroll, { name: 'vue3SeamlessScroll' })
 InitCheckResult(app)
 InitVariable(app)
-InitDirective(app)
+app.mount('#app')

+ 0 - 29
src/utils/directives.js

@@ -1,29 +0,0 @@
-import { UserStore } from '@/store/user'
-import { get, isArray } from 'lodash-es'
-import router from '@/router'
-const InitDirective = (app) => {
-  app.directive('method', {
-    mounted(el, binding) {
-      const { user } = UserStore()
-      const { value: code } = binding
-      const rUser = toRaw(user)
-      // 超级管理员不进行检查
-      if (get(rUser, 'role') === 'Admin' && get(rUser, 'is_super') === '0') return
-      const roleCode = get(rUser, 'role_code')
-      // 需要判断roleCode中是否有这个权限.但是权限又需要路由拼接起来,最少也得有上层级组合
-      const cr = router.currentRoute.value
-      // 拼接当前路由和层级的name
-      const rArr = cr.matched.filter((f) => f.name !== 'Layout').map((i) => i.name)
-      rArr.push(code)
-      const thisMethodCode = `${rArr.join('.')}`
-      if (!isArray(roleCode)) {
-        el.parentNode.removeChild(el)
-      }
-      if (!roleCode.includes(thisMethodCode)) {
-        el.parentNode.removeChild(el)
-      }
-    }
-  })
-}
-
-export { InitDirective }

+ 19 - 8
src/utils/error-code.js

@@ -1,5 +1,3 @@
-import i18n from '@/lang/index'
-
 const UNKNOW = 'UNKNOW'
 const BADPARAM = 'BADPARAM'
 const NETWORK = 'NETWORK'
@@ -38,12 +36,25 @@ const ErrorCode = {
   [BUSINESS]: -100
 }
 
-const errmsg = (err) => {
-  const ErrorMessage = i18n.global.t('ErrorMessage')
-  for (const key in ErrorMessage) {
-    if (ErrorCode[key] === err) {
-      return ErrorMessage[key]
-    }
+const errmsg = () => {
+  return {
+    UNKNOW: 'unknow',
+    BADPARAM: 'bad param',
+    NETWORK: 'network',
+    JSON_ERROR: 'json error',
+    USER_NOT_EXIST: 'user not exist',
+    BAD_PASSWORD: 'bad password',
+    NOT_LOGIN: 'not login',
+    ACCESS_DENIED: 'access denied',
+    DATA_NOT_EXIST: 'data not exist',
+    DATA_EXISTED: 'data existed',
+    DATA_INVALID: 'data invalid',
+    VERIFYCODE_INVALID: 'verifycode invalid',
+    SERVICE_FAULT: 'service fault',
+    DATABASE_FAULT: 'database fault',
+    FILE_FAULT: 'file fault',
+    USER_NOT_BIND: 'user not bind',
+    BUSINESS: 'business error'
   }
 }
 

+ 0 - 12
src/utils/i18n.js

@@ -1,12 +0,0 @@
-// translate router.meta.title, be used in breadcrumb sidebar tagsview
-import i18n from '@/lang/index'
-
-export function translateRouteTitle(title) {
-  // 判断是否存在国际化配置,如果没有原生返回
-  const hasKey = i18n.global.te('menus.' + title)
-  if (hasKey) {
-    const translatedTitle = i18n.global.t('menus.' + title)
-    return translatedTitle
-  }
-  return title
-}

+ 1 - 1
src/views/company/detail.vue

@@ -76,7 +76,7 @@
               <el-col :span="24" class="content">
                 <a-list :loading="loading" :grid="{ gutter: 16, column: 4 }" :data-source="list">
                   <template #renderItem="{ item }">
-                    <el-col :span="24" class="list" @click="toView(item)">
+                    <el-col :span="24" class="list">
                       <el-col :span="24" class="name textOver">
                         <el-tooltip effect="dark" :content="item.name" placement="top">
                           {{ item.name || '暂无名称' }}

+ 1 - 1
src/views/expert/detail.vue

@@ -49,7 +49,7 @@
               <el-col :span="24" class="content">
                 <a-list :loading="loading" :grid="{ gutter: 16, column: 4 }" :data-source="list">
                   <template #renderItem="{ item }">
-                    <el-col :span="24" class="list" @click="toView(item)">
+                    <el-col :span="24" class="list">
                       <el-col :span="24" class="name textOver">
                         <el-tooltip effect="dark" :content="item.name" placement="top">
                           {{ item.name || '暂无名称' }}

+ 1 - 1
src/views/home/index.vue

@@ -154,7 +154,7 @@
                       {{ item.direction || '暂无研究方向' }}
                     </el-col>
                     <el-col :span="4" class="content textOver">
-                      <el-button @click="toChat(item)" type="primary" size="small">
+                      <el-button @click.stop="toChat(item)" type="primary" size="small">
                         联络专家
                       </el-button>
                     </el-col>

+ 2 - 2
src/views/index/index.vue

@@ -233,8 +233,8 @@ const toLogout = () => {
 
         .two_1 {
           position: absolute;
-          top: 42%;
-          left: 34%;
+          top: 45%;
+          left: 35%;
           display: flex;
           flex-direction: column;
           align-items: center;

+ 119 - 18
src/views/search/index.vue

@@ -13,15 +13,31 @@
           </div>
         </el-col>
         <el-col :span="24" class="one">
-          <a-tabs v-model:activeKey="activeKey" centered>
-            <a-tab-pane key="1" tab="成果(0)">成果</a-tab-pane>
-            <a-tab-pane key="2" tab="需求(0)">需求</a-tab-pane>
-            <a-tab-pane key="3" tab="项目(0)">项目</a-tab-pane>
-            <a-tab-pane key="4" tab="赛事(0)">赛事</a-tab-pane>
-            <a-tab-pane key="5" tab="资讯(0)">资讯</a-tab-pane>
-            <a-tab-pane key="6" tab="单位(0)">单位</a-tab-pane>
-            <a-tab-pane key="7" tab="企业(0)">企业</a-tab-pane>
-            <a-tab-pane key="8" tab="专家(0)">专家</a-tab-pane>
+          <a-tabs v-model:activeKey="activeKey" centered @change="toChange">
+            <a-tab-pane key="1" :tab="`成果(${total})`">
+              <List></List>
+            </a-tab-pane>
+            <a-tab-pane key="2" :tab="`需求(${total})`">
+              <List></List>
+            </a-tab-pane>
+            <a-tab-pane key="3" :tab="`项目(${total})`">
+              <List></List>
+            </a-tab-pane>
+            <a-tab-pane key="4" :tab="`赛事(${total})`">
+              <List></List>
+            </a-tab-pane>
+            <a-tab-pane key="5" :tab="`资讯(${total})`">
+              <List></List>
+            </a-tab-pane>
+            <a-tab-pane key="6" :tab="`单位(${total})`">
+              <List></List>
+            </a-tab-pane>
+            <a-tab-pane key="7" :tab="`企业(${total})`">
+              <List></List>
+            </a-tab-pane>
+            <a-tab-pane key="8" :tab="`专家(${total})`">
+              <List></List>
+            </a-tab-pane>
           </a-tabs>
         </el-col>
       </el-col>
@@ -33,34 +49,119 @@
 // 基础
 import { get } from 'lodash-es'
 const $checkRes = inject('$checkRes')
+import List from './parts/list.vue'
 import { SearchOutlined } from '@ant-design/icons-vue'
 // 接口
+import { AchievementStore } from '@/store/api/platform/achievement'
 import { DemandStore } from '@/store/api/platform/demand'
 import { DictDataStore } from '@/store/api/system/dictData'
-const store = DemandStore()
+const achieveStore = AchievementStore()
+const demandStore = DemandStore()
 const dictDataStore = DictDataStore()
 // 图片引入
 import searchOne from '@/assets/search.jpeg'
 // 路由
-const route = useRoute()
+const router = useRouter()
 // 加载中
 const loading = ref(false)
 const activeKey = ref('1')
+// const actList = ref([
+//   { label: '成果', value: 'achievement' },
+//   { label: '需求', value: 'demand' },
+//   { label: '项目', value: 'project' },
+//   { label: '赛事', value: 'match' },
+//   { label: '资讯', value: 'news' },
+//   { label: '单位', value: 'unit' },
+//   { label: '企业', value: 'company' },
+//   { label: '专家', value: 'expert' }
+// ])
+const searchForm = ref({})
+// 字典表
+const fieldList = ref([])
+//
+const fields = ref([
+  { label: '名称', model: 'name', type: 'name' },
+  { label: '技术领域:', model: 'field', type: 'dict' },
+  { label: '成果地区:', model: 'area', type: 'area' },
+  { label: '单位:', model: 'user' }
+])
+// 列表
+const list = ref([])
+let skip = 0
+let limit = inject('limit')
+const total = ref(0)
+const currentPage = ref(1)
 // 请求
 onMounted(async () => {
   loading.value = true
   await searchOther()
-  await search()
+  await search({ skip, limit })
   loading.value = false
 })
-const search = async () => {
-  let id = route.query.id
-  if (id) {
-    // let res = await store.fetch(id)
-    // if (res.errcode == '0') info.value = res.data
+const search = async (query = { skip: 0, limit }) => {
+  const info = {
+    skip: query.skip,
+    limit: query.limit,
+    ...searchForm.value,
+    is_use: '0',
+    status: '1'
   }
+  let res
+  if (activeKey.value == '1') res = await achieveStore.query(info)
+  else if (activeKey.value == '2') res = await demandStore.query(info)
+  if (res.errcode == '0') {
+    list.value = res.data
+    total.value = res.total
+  }
+}
+const searchOther = async () => {
+  let result
+  // 技术领域
+  result = await dictDataStore.query({ code: 'field', is_use: '0' })
+  if ($checkRes(result)) fieldList.value = result.data
+}
+// 字典数据转换
+const getDict = (data, model) => {
+  let res
+  if (model == 'field') res = fieldList.value.find((f) => f.value == data)
+  return get(res, 'label')
+}
+// 地区显示
+const getArea = (data) => {
+  if (data) return data.join(',')
+}
+// 查看
+const toView = (item) => {
+  let path
+  if (activeKey.value == '1') path = '/achievement/detail'
+  else if (activeKey.value == '2') path = '/demand/detail'
+  router.push({ path, query: { id: item.id || item._id } })
+}
+// 切换标签页
+const toChange = (key) => {
+  activeKey.value = key
+  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 })
 }
-const searchOther = async () => {}
+// provide
+provide('fields', fields)
+provide('list', list)
+provide('toView', toView)
+provide('getArea', getArea)
+provide('getDict', getDict)
+provide('limit', limit)
+provide('total', total)
+provide('currentPage', currentPage)
+provide('sizeChange', sizeChange)
+provide('changePage', changePage)
 </script>
 <style scoped lang="scss">
 .main {

+ 145 - 0
src/views/search/parts/list.vue

@@ -0,0 +1,145 @@
+<template>
+  <div id="index">
+    <el-row>
+      <el-col :span="24" class="main">
+        <div class="w_1200">
+          <el-col :span="24" class="one">
+            <a-list :loading="loading" :grid="{ gutter: 14, column: 4 }" :data-source="list">
+              <template #renderItem="{ item }">
+                <el-col :span="24" class="list">
+                  <el-col :span="24" v-for="(i, f) in fields" :key="f">
+                    <el-col :span="24" class="name textOver" v-if="i.type == 'name'">
+                      <el-tooltip effect="dark" :content="item[i.model]" placement="top">
+                        {{ item[i.model] || '暂无' }}
+                      </el-tooltip>
+                    </el-col>
+                    <el-col :span="24" class="other_1" v-else-if="i.type == 'dict'">
+                      <span>{{ i.label }}</span>
+                      {{ getDict(item[i.model] || '暂无', 'field') }}
+                    </el-col>
+                    <el-col :span="24" class="other_1" v-else-if="i.type == 'area'">
+                      <span>{{ i.label }}</span>
+                      {{ getArea(item[i.model] || '暂无') }}
+                    </el-col>
+                    <el-col :span="24" v-else class="other_1 textOver">
+                      <span>{{ i.label }}</span>
+                      {{ item[i.model] || '暂无' }}
+                    </el-col>
+                  </el-col>
+                  <el-col :span="24" class="bottom">
+                    <el-button @click.stop="toView(item)" type="primary" size="small"
+                      >查看详情</el-button
+                    >
+                  </el-col>
+                </el-col>
+              </template>
+            </a-list>
+          </el-col>
+          <el-col :span="24" class="two">
+            <el-pagination
+              background
+              layout="total, prev, pager, next"
+              :page-sizes="[10, 20, 50, 100, 200]"
+              :total="total"
+              :page-size="limit"
+              v-model:current-page="currentPage"
+              @current-change="changePage"
+              @size-change="sizeChange"
+            >
+            </el-pagination>
+          </el-col>
+        </div>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script setup>
+const fields = inject('fields')
+const list = inject('list')
+const total = inject('total')
+const limit = inject('limit')
+const getDict = inject('getDict')
+const getArea = inject('getArea')
+const toView = inject('toView')
+const currentPage = inject('currentPage')
+const changePage = inject('changePage')
+const sizeChange = inject('sizeChange')
+</script>
+<style scoped lang="scss">
+.main {
+  margin: 15px;
+  .one {
+    .list {
+      background: #fff;
+      box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.03);
+      border-radius: 2px;
+      width: 285px;
+      height: 195px;
+      margin-bottom: 15px;
+      cursor: pointer;
+      transition: all 0.3s;
+      padding: 20px;
+
+      .name {
+        display: flex;
+        align-items: center;
+        font-size: 16px;
+        color: #121834;
+        font-weight: 500;
+        margin: 5px 0 0 0;
+        .tags {
+          grid-gap: 0.5rem;
+          gap: 0.5rem;
+          display: flex;
+          margin: 0 0 0 10px;
+        }
+      }
+
+      .name:hover {
+        color: #2374ff;
+      }
+
+      .other_1 {
+        font-size: 12px;
+        text-align: justify;
+        line-height: 12px;
+        font-weight: 400;
+        letter-spacing: 0;
+        color: #8f97a3;
+        margin-top: 10px;
+
+        span:last-child {
+          color: #525a68;
+        }
+      }
+
+      .bottom {
+        margin: 20px 10px;
+        text-align: right;
+        .button {
+          font-size: 12px;
+          color: #fff;
+          text-align: center;
+          line-height: 12px;
+          font-weight: 500;
+          padding: 5px 12px;
+          background: #2374ff;
+          box-shadow: 0 3px 6px 0 rgba(35, 116, 255, 0.1);
+          border-radius: 2px;
+        }
+      }
+    }
+
+    .list:hover {
+      background: #f0f7ff;
+      box-shadow: 0 0 16px rgba(205, 205, 205, 0.6);
+    }
+  }
+  .two {
+    display: flex;
+    flex-direction: row-reverse;
+    padding: 20px;
+  }
+}
+</style>