Selaa lähdekoodia

change: 产品搜索页面

kindring 2 vuotta sitten
vanhempi
sitoutus
44e02efffc

+ 1 - 1
components/layout/adminLayout.vue

@@ -171,7 +171,7 @@ export default {
 <!--          侧边栏,带标题,icon,下拉菜单-->
       <div v-for="(item,i) in sideMenus"
            :key="'sideBar-'+i"
-           :class="`bar-menu-item ${item.isShow?'bar-menu-info-show':''}`"
+           :class="`bar-menu-item ${item.isShow?'bar-menu-item-show':''}`"
       >
         <div class="menu-title">
           <span class="icon"> <svg-icon :icon-class="item.icon"/> </span>

+ 81 - 0
components/public/tableSelect.vue

@@ -0,0 +1,81 @@
+<template>
+  <a-select
+      @change="changeSelected"
+      :value="val===''?undefined:val"
+      @blur="$emit('blur')"
+      @focus="$emit('focus')"
+  >
+    <a-select-option
+        v-for="option in options"
+        :key="`${keystr}-${option.key}`"
+        :placeholder="placeholder"
+        :disabled="!!disableds.find(val=>val===option.key)"
+        :class="{'bg-blue-300':option.value===val}"
+    >
+      {{option.text}}
+    </a-select-option>
+  </a-select>
+</template>
+
+<script>
+export default {
+  name: "tableSelect",
+  props: {
+    options:{
+      default:[]
+    },
+    value: {
+      default: ''
+    },
+    keystr: {
+      default: 'default-key'
+    },
+    placeholder:{
+      default: ''
+    },
+    disableds:{
+      default: function (){return []}
+    }
+  },
+  beforeMount() {
+    if(this.value){
+      this.val = this.keystr+'-'+this.value;
+    }else{
+      this.val = null;
+    }
+  },
+  watch:{
+    value(newVal){
+      // console.log(newVal)
+      // console.log('11111111')
+      if(!newVal){
+        this.val = newVal;
+      }else{
+        this.val = this.keystr+'-'+newVal;
+      }
+    }
+  },
+  data(){
+    return {
+      val:null,
+    }
+  },
+  methods: {
+    changeSelected(str){
+      this.val = str;
+      str = str.replace(this.keystr+'-','');
+      let rawData = this.options.find((item)=>item.key === str);
+      console.log(str)
+      console.log(rawData)
+      this.$emit('input',str);
+      this.$emit('change',str);
+      this.$emit('raw',rawData);
+
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 28 - 2
map/adminSideBar.js

@@ -31,14 +31,40 @@ export const adminMenus = [
     child: [
       {
         key: 'searchProduct',
-        title: '产品检索',
-        path: '/manger/product/search'
+        title: '产品中心',
+        path: '/manger/product'
       },
       {
         key: 'addProduct',
         title: '新增产品',
         path: '/manger/product/add'
       },
+      {
+        key: 'productType',
+        title: '产品类别管理',
+        path: '/manger/product/type'
+      }
+    ],
+  },
+  {
+    title: '文章管理',
+    icon: 'news-setting',
+    child: [
+      {
+        key: 'searchPages',
+        title: '文章中心',
+        path: '/manger/product'
+      },
+      {
+        key: 'searchSolution',
+        title: '新闻中心',
+        path: '/manger/product'
+      },
+      {
+        key: 'productType',
+        title: '产品类别管理',
+        path: '/manger/product/type'
+      }
     ],
   }
 ]

+ 4 - 0
map/apiMap.js

@@ -34,6 +34,10 @@ export const apiMap = {
   searchNews: {
     path: `/api/news/search`,
   },
+  // 获取所有文章接口
+  searchAllNews: {
+    path: `/api/news/pages`,
+  },
   searchNewsMini: {
     path: `/api/news/mini`,
   },

+ 93 - 0
pages/manger/news.vue

@@ -0,0 +1,93 @@
+<script>
+
+
+import AdminLayout from "~/components/layout/adminLayout.vue";
+import {adminMenus} from "../../map/adminSideBar";
+import Vue from 'vue'
+import antd from 'ant-design-vue'
+import 'ant-design-vue/dist/antd.css'
+import {login_types} from "../../store/login";
+import handle from "../../until/handle";
+Vue.use(antd);
+
+export default {
+  name: "mangerProduct",
+  components: {AdminLayout},
+  async asyncData(ctx) {
+    // 判断 store 中的 isLogin 是否为 true $store.state.login.captcha
+    console.log(ctx?.store?.state?.login)
+    if (!ctx?.store?.state?.login?.isLogin) {
+      // 如果未登录,重定向到登录页
+      // ctx.redirect('/manger/login')
+      ctx.redirect('/manger/login')
+    }
+    return {}
+  },
+  data(){
+    return {
+      headerMenus:[
+        {
+          text: '前往首页',
+          href: '/',
+          key: 'toIndex'
+        }
+      ],
+      sidebarMenus: adminMenus
+
+    }
+  },
+  computed: {
+    userInfo(){
+      return this.$store.state.login.userInfo
+    }
+  },
+  methods: {
+    dataLogOut(){
+      this.$store.commit('login/'+login_types.mutations.userLogout);
+    },
+    async logout(){
+      let [err,res] = await handle(this.$axios.get('/api/user/logout'));
+      this.dataLogOut();
+      if(err){
+        return {};
+      }
+      this.$message.success('退出成功');
+      // 切换url至 /login
+      this.$router.push('/manger/login');
+    }
+  }
+}
+</script>
+
+<template>
+  <div class="w-full">
+    <admin-layout
+      :logoTitle="'深圳合方圆站点管理'"
+      :header-menus="headerMenus"
+      :sidebar-menus="sidebarMenus"
+      index-path="/manger"
+    >
+      <!--  下拉菜单,对应slot user-->
+      <a-dropdown slot="user">
+        <a class="ant-dropdown-link" href="#">
+          {{ userInfo.name }} <a-icon type="down" />
+        </a>
+        <a-menu slot="overlay">
+          <!--      <a-menu-info key="1">-->
+          <!--        <a href="#">1st menu info</a>-->
+          <!--      </a-menu-info>-->
+          <a-menu-item key="3">
+            <span @click="logout">退出登录</span>
+          </a-menu-item>
+        </a-menu>
+      </a-dropdown>
+      <nuxt-child>
+
+      </nuxt-child>
+    </admin-layout>
+  </div>
+</template>
+
+<style scoped>
+
+</style>

+ 286 - 0
pages/manger/news/index.vue

@@ -0,0 +1,286 @@
+<script>
+import handle from "../../../until/handle";
+import axios from "axios";
+import {apiMap, baseUrl} from "../../../map/apiMap";
+import {rCode} from "../../../map/rcodeMap_esm";
+import RoundedTitle from "../../../components/public/roundedTitle.vue";
+import copy from "../../../until/copy";
+import {isEmpty} from "../../../until/typeTool";
+import TableSelect from "../../../components/public/tableSelect.vue";
+import dbField_esm from "../../../map/dbField_esm";
+import ImageViewer from "../../../components/public/imageViewer.vue";
+
+const newsColumns = [
+  {
+    title: "ID",
+    dataIndex: 'id',
+    width: '5%',
+  },
+  {
+    title: "文章标题",
+    dataIndex: 'title',
+    width: '20%',
+  },
+  {
+    title: "文章类型",
+    dataIndex: 'type_key',
+    width: '20%',
+    scopedSlots: {customRender: 'newsType'},
+  },
+  {
+    title: "文章封面",
+    dataIndex: 'coverPath',
+    width: '20%',
+    scopedSlots: {customRender: 'image'},
+  },
+  {
+    title: "操作",
+    scopedSlots: {customRender: 'operation'},
+    width: '25%',
+  },
+]
+export default {
+  name: 'news',
+  components: {ImageViewer, TableSelect, RoundedTitle},
+  computed: {
+    // 产品类型数据
+    newsTypes(){
+      let arr = this.$store.getters.allNewsTypes;
+      // 添加 all
+      arr.unshift({text: '全部', key: 'all' });
+      return arr;
+    },
+  },
+  data(){
+    return {
+      loading: false,
+      newsColumns: newsColumns,
+      dataList: [],
+      page: 1,
+      pageSize: 10,
+      total: 0,
+      form: {
+        pType: {
+          val: '',
+          oldVal: '',
+          init: '',
+          msg: '',
+          state: 0,
+          options: [
+
+          ]
+        },
+        key: {
+          val: '',
+          oldVal: '',
+          init: '',
+          msg: '',
+          state: 0
+        },
+        type: {
+          val: '',
+          init: '',
+          msg: '',
+          state: 0,
+          options: []
+        },
+      },
+      loadErr: false,
+    }
+  },
+  async asyncData(){
+    let searchParam = {
+      page: 1,
+      pageSize: 10,
+      key: '',
+      type: 'all'
+    };
+    console.log('搜索产品数据');
+    let [err,res] = await handle(axios.get(baseUrl + apiMap.searchProduct.path,{params: searchParam}));
+    if(err){
+      console.error(err);
+      return {};
+    }
+    let result = res.data;
+    if(result.code === rCode.OK){
+      return {
+        dataList: result.data,
+        page: 1,
+        pageSize: 10,
+        total: result.total,
+      }
+    }else{
+      console.log(`搜索数据失败,${result.msg}`);
+      return {}
+    }
+    return {}
+  },
+  beforeMount() {
+    this.form.type.options = this.newsTypes;
+    this.form.type.val = this.form.type.options[0].key;
+    this.form.type.oldVal = this.form.type.val;
+    this.form.type.init = this.form.type.val;
+  },
+  methods: {
+    copy(str){
+      copy(str).then(()=>{
+        this.$message.success('已经将内容复制到剪切板')
+      }).catch(err=>{
+        console.error(err.message);
+        this.$message.error('内容复制失败,请手动复制内容')
+      })
+    },
+    keyLight(str){
+      return str.replace(this.form.key.oldVal,`<span class="text-red-500 text-xl">${this.form.key.oldVal}</span>`)
+    },
+    // 搜索
+    async searchExecute(params = {}){
+      let err,res;
+      let url = apiMap.searchNews.path;
+      console.log(params);
+      // 如果没有传入参数,则使用表单数据
+      if(isEmpty(params.key)){
+        params.key = this.form.key.oldVal;
+      }
+      if(isEmpty(params.type)){
+        params.type = this.form.type.oldVal;
+      }
+      params.p = this.page;
+      params.l = this.pageSize;
+      console.log(`搜索产品数据:${JSON.stringify(params)}`);
+      this.loading = true;
+      [err,res] = await handle(axios.get(url,{params}));
+      this.loading = false;
+      if(err){
+        return this.$message.error(err.message);
+      }
+      let result = res.data;
+      if(result.code === rCode.OK){
+        console.log(`搜索成功`)
+        this.form.key.oldVal = params.key;
+        this.form.type.oldVal = params.type;
+        if (params.p === 1){
+          this.$message.success('搜索成功');
+          this.total = result.total;
+        }
+        // 触发更新,设置数据
+        this.$nextTick(()=>{
+          this.dataList = result.data;
+        })
+
+
+      }else{
+        this.$message.error(result.msg);
+      }
+    },
+    // 页码改变
+    pageChange(page){
+      console.log(`页码改变:${page}`);
+      this.page = page;
+      console.log(this.page);
+      this.searchExecute();
+    },
+    // 页数改变
+    pageSizeChange(pageSize){
+      console.log('页数改变');
+      this.pageSize = pageSize;
+      this.searchExecute();
+    },
+    onSearchHandle(){
+      this.page = 1;
+      console.log(this.form.type.val);
+      this.searchExecute({
+        key: this.form.key.val,
+        type: this.form.type.val,
+      });
+    },
+    refreshHandel(){
+      this.page = 1;
+      this.searchExecute();
+    },
+    getProTypeText(key){
+      let type = this.newsTypes.find(item=>item.key === key);
+      return type ? type.text : '';
+    },
+
+  }
+}
+</script>
+
+<template>
+  <div class="w-full p-2">
+    <rounded-title class="text-xl">
+      <span>产品管理,产品中心</span>
+      <a href="/manger/product/type" class="px-10 h-full ml-5 rounded bg-blue-400 text-white cursor-pointer hover:text-orange-500 ">产品分类管理</a>
+    </rounded-title>
+
+    <div class="w-full rounded border mt-2 bg-white p-2">
+      <div class="w-full flex h-14 items-center justify-between border-b">
+
+        <div class="flex">
+          <table-select
+            class="w-48 mx-3"
+            :options="form.type.options"
+            v-model="form.type.val"
+          />
+          <a-input-search class="!ml-2 w-64" type="text" allow-clear placeholder="产品关键字"
+                          @search="onSearchHandle"
+                          :loading="loading" v-model="form.key.val" />
+        </div>
+
+
+        <div class="flex">
+          <a-button class="ml-2" type="primary" @click="refreshHandel" :loading="loading">刷新</a-button>
+        </div>
+
+      </div>
+      <div class="w-full">
+        <a-table
+          :columns="newsColumns"
+          :loading="loading"
+          :row-key="record => record.id"
+          :pagination="false"
+          :data-source="dataList"
+        >
+          <template slot="proType" slot-scope="text">
+            <span>{{getProTypeText(text)}}</span>
+          </template>
+
+          <template slot="image" slot-scope="text,record">
+            <image-viewer :src="text" :alt="record.name" />
+          </template>
+
+          <template slot="keyLight" class="flex" slot-scope="text">
+            <div class="w-full flex items-center">
+              <div  v-html="keyLight(text)"></div>
+              <svg-icon :name="'copy'" :icon-class="'copy'" class="text-2xl cursor-pointer mx-2" title="点击复制" @click.native="copy(text)" />
+            </div>
+          </template>
+          <template class="flex justify-center" slot="operation" slot-scope="text,record">
+            <a-button  type="primary">
+              <a :href="`/manger/product/info?id=${record.id}`">产品详情</a>
+            </a-button>
+
+            <a-button class="mx-3" type="primary">
+              <a :href="`/manger/product/edit?id=${record.id}`">编辑产品</a>
+            </a-button>
+
+          </template>
+
+        </a-table>
+        <a-pagination
+          class="mt-2"
+          :total="total"
+          :page-size="pageSize"
+          :current="page"
+          @change="pageChange"
+          @show-size-change="pageSizeChange"
+        />
+      </div>
+    </div>
+  </div>
+</template>
+
+<style scoped>
+
+</style>

+ 93 - 0
pages/manger/product.vue

@@ -0,0 +1,93 @@
+<script>
+
+
+import AdminLayout from "~/components/layout/adminLayout.vue";
+import {adminMenus} from "../../map/adminSideBar";
+import Vue from 'vue'
+import antd from 'ant-design-vue'
+import 'ant-design-vue/dist/antd.css'
+import {login_types} from "../../store/login";
+import handle from "../../until/handle";
+Vue.use(antd);
+
+export default {
+  name: "mangerProduct",
+  components: {AdminLayout},
+  async asyncData(ctx) {
+    // 判断 store 中的 isLogin 是否为 true $store.state.login.captcha
+    console.log(ctx?.store?.state?.login)
+    if (!ctx?.store?.state?.login?.isLogin) {
+      // 如果未登录,重定向到登录页
+      // ctx.redirect('/manger/login')
+      ctx.redirect('/manger/login')
+    }
+    return {}
+  },
+  data(){
+    return {
+      headerMenus:[
+        {
+          text: '前往首页',
+          href: '/',
+          key: 'toIndex'
+        }
+      ],
+      sidebarMenus: adminMenus
+
+    }
+  },
+  computed: {
+    userInfo(){
+      return this.$store.state.login.userInfo
+    }
+  },
+  methods: {
+    dataLogOut(){
+      this.$store.commit('login/'+login_types.mutations.userLogout);
+    },
+    async logout(){
+      let [err,res] = await handle(this.$axios.get('/api/user/logout'));
+      this.dataLogOut();
+      if(err){
+        return {};
+      }
+      this.$message.success('退出成功');
+      // 切换url至 /login
+      this.$router.push('/manger/login');
+    }
+  }
+}
+</script>
+
+<template>
+  <div class="w-full">
+    <admin-layout
+      :logoTitle="'深圳合方圆站点管理'"
+      :header-menus="headerMenus"
+      :sidebar-menus="sidebarMenus"
+      index-path="/manger"
+    >
+      <!--  下拉菜单,对应slot user-->
+      <a-dropdown slot="user">
+        <a class="ant-dropdown-link" href="#">
+          {{ userInfo.name }} <a-icon type="down" />
+        </a>
+        <a-menu slot="overlay">
+          <!--      <a-menu-info key="1">-->
+          <!--        <a href="#">1st menu info</a>-->
+          <!--      </a-menu-info>-->
+          <a-menu-item key="3">
+            <span @click="logout">退出登录</span>
+          </a-menu-item>
+        </a-menu>
+      </a-dropdown>
+      <nuxt-child>
+
+      </nuxt-child>
+    </admin-layout>
+  </div>
+</template>
+
+<style scoped>
+
+</style>

+ 276 - 0
pages/manger/product/index.vue

@@ -0,0 +1,276 @@
+<script>
+import handle from "../../../until/handle";
+import axios from "axios";
+import {apiMap, baseUrl} from "../../../map/apiMap";
+import {rCode} from "../../../map/rcodeMap_esm";
+import RoundedTitle from "../../../components/public/roundedTitle.vue";
+import copy from "../../../until/copy";
+import {isEmpty} from "../../../until/typeTool";
+import TableSelect from "../../../components/public/tableSelect.vue";
+import dbField_esm from "../../../map/dbField_esm";
+import ImageViewer from "../../../components/public/imageViewer.vue";
+
+const productColumns = [
+  {
+    title: 'ID',
+    dataIndex: 'id',
+    width: '5%',
+  },
+  {
+    title: '产品名称',
+    dataIndex: 'name',
+    width: '20%',
+  },
+  {
+    title: '产品类型',
+    dataIndex: 'type_key',
+    width: '20%',
+    scopedSlots: {customRender: 'peoType'},
+  },
+  {
+    title: '产品图片',
+    dataIndex: 'image',
+    width: '20%',
+    scopedSlots: {customRender: 'image'},
+  },
+  {
+    title: '操作',
+    scopedSlots: {customRender: 'operation'},
+    width: '25%',
+  },
+]
+export default {
+  name: 'product',
+  components: {ImageViewer, TableSelect, RoundedTitle},
+  computed: {
+    // 产品类型数据
+    productTypes(){
+      let arr = this.$store.getters.productTypes;
+      // 添加 all
+      arr.unshift({text: '全部', key: 'all' });
+      return arr;
+    },
+  },
+  data(){
+    return {
+      loading: false,
+      productColumns: productColumns,
+      productionList: [],
+      page: 1,
+      pageSize: 10,
+      total: 0,
+      form: {
+        key: {
+          val: '',
+          oldVal: '',
+          init: '',
+          msg: '',
+          state: 0
+        },
+        type: {
+          val: '',
+          init: '',
+          msg: '',
+          state: 0,
+          options: []
+        },
+      },
+      loadErr: false,
+    }
+  },
+  async asyncData(){
+    let searchParam = {
+      page: 1,
+      pageSize: 10,
+      key: '',
+      type: 'all'
+    };
+    console.log('搜索产品数据');
+    let [err,res] = await handle(axios.get(baseUrl + apiMap.searchProduct.path,{params: searchParam}));
+    if(err){
+      console.error(err);
+      return {};
+    }
+    let result = res.data;
+    if(result.code === rCode.OK){
+      return {
+        productionList: result.data,
+        page: 1,
+        pageSize: 10,
+        total: result.total,
+      }
+    }else{
+      console.log(`搜索数据失败,${result.msg}`);
+      return {}
+    }
+    return {}
+  },
+  beforeMount() {
+    this.form.type.options = this.productTypes;
+    this.form.type.val = this.form.type.options[0].key;
+    this.form.type.oldVal = this.form.type.val;
+    this.form.type.init = this.form.type.val;
+  },
+  methods: {
+    copy(str){
+      copy(str).then(()=>{
+        this.$message.success('已经将内容复制到剪切板')
+      }).catch(err=>{
+        console.error(err.message);
+        this.$message.error('内容复制失败,请手动复制内容')
+      })
+    },
+    keyLight(str){
+      return str.replace(this.form.key.oldVal,`<span class="text-red-500 text-xl">${this.form.key.oldVal}</span>`)
+    },
+    // 搜索
+    async searchExecute(params = {}){
+      let err,res;
+      let url = apiMap.searchProduct.path;
+      console.log(params);
+      // 如果没有传入参数,则使用表单数据
+      if(isEmpty(params.key)){
+        params.key = this.form.key.oldVal;
+      }
+      if(isEmpty(params.type)){
+        params.type = this.form.type.oldVal;
+      }
+      params.p = this.page;
+      params.l = this.pageSize;
+      console.log(`搜索产品数据:${JSON.stringify(params)}`);
+      this.loading = true;
+      [err,res] = await handle(axios.get(url,{params}));
+      this.loading = false;
+      if(err){
+        return this.$message.error(err.message);
+      }
+      let result = res.data;
+      if(result.code === rCode.OK){
+        console.log(`搜索成功`)
+        this.form.key.oldVal = params.key;
+        this.form.type.oldVal = params.type;
+        if (params.p === 1){
+          this.$message.success('搜索成功');
+          this.total = result.total;
+        }
+        // 触发更新,设置数据
+        this.$nextTick(()=>{
+          this.productionList = result.data;
+        })
+
+
+      }else{
+        this.$message.error(result.msg);
+      }
+    },
+    // 页码改变
+    pageChange(page){
+      console.log(`页码改变:${page}`);
+      this.page = page;
+      console.log(this.page);
+      this.searchExecute();
+    },
+    // 页数改变
+    pageSizeChange(pageSize){
+      console.log('页数改变');
+      this.pageSize = pageSize;
+      this.searchExecute();
+    },
+    onSearchHandle(){
+      this.page = 1;
+      console.log(this.form.type.val);
+      this.searchExecute({
+        key: this.form.key.val,
+        type: this.form.type.val,
+      });
+    },
+    refreshHandel(){
+      this.page = 1;
+      this.searchExecute();
+    },
+    getProTypeText(key){
+      let type = this.productTypes.find(item=>item.key === key);
+      return type ? type.text : '';
+    },
+
+  }
+}
+</script>
+
+<template>
+  <div class="w-full p-2">
+    <rounded-title class="text-xl">
+      <span>产品管理,产品中心</span>
+      <a href="/manger/product/type" class="px-10 h-full ml-5 rounded bg-blue-400 text-white cursor-pointer hover:text-orange-500 ">产品分类管理</a>
+    </rounded-title>
+
+    <div class="w-full rounded border mt-2 bg-white p-2">
+      <div class="w-full flex h-14 items-center justify-between border-b">
+
+        <div class="flex">
+          <table-select
+            class="w-48 mx-3"
+            :options="form.type.options"
+            v-model="form.type.val"
+          />
+          <a-input-search class="!ml-2 w-64" type="text" allow-clear placeholder="产品关键字"
+                          @search="onSearchHandle"
+                          :loading="loading" v-model="form.key.val" />
+        </div>
+
+
+        <div class="flex">
+          <a-button class="ml-2" type="primary" @click="refreshHandel" :loading="loading">刷新</a-button>
+        </div>
+
+      </div>
+      <div class="w-full">
+        <a-table
+          :columns="productColumns"
+          :loading="loading"
+          :row-key="record => record.id"
+          :pagination="false"
+          :data-source="productionList"
+        >
+          <template slot="proType" slot-scope="text">
+            <span>{{getProTypeText(text)}}</span>
+          </template>
+
+          <template slot="image" slot-scope="text,record">
+            <image-viewer :src="text" :alt="record.name" />
+          </template>
+
+          <template slot="keyLight" class="flex" slot-scope="text">
+            <div class="w-full flex items-center">
+              <div  v-html="keyLight(text)"></div>
+              <svg-icon :name="'copy'" :icon-class="'copy'" class="text-2xl cursor-pointer mx-2" title="点击复制" @click.native="copy(text)" />
+            </div>
+          </template>
+          <template class="flex justify-center" slot="operation" slot-scope="text,record">
+            <a-button  type="primary">
+              <a :href="`/manger/product/info?id=${record.id}`">产品详情</a>
+            </a-button>
+
+            <a-button class="mx-3" type="primary">
+              <a :href="`/manger/product/edit?id=${record.id}`">编辑产品</a>
+            </a-button>
+
+          </template>
+
+        </a-table>
+        <a-pagination
+          class="mt-2"
+          :total="total"
+          :page-size="pageSize"
+          :current="page"
+          @change="pageChange"
+          @show-size-change="pageSizeChange"
+          />
+      </div>
+    </div>
+  </div>
+</template>
+
+<style scoped>
+
+</style>

+ 17 - 0
pages/manger/product/type.vue

@@ -0,0 +1,17 @@
+<script setup>
+
+import RoundedTitle from "../../../components/public/roundedTitle.vue";
+</script>
+
+<template>
+  <div class="w-full p-2">
+    <rounded-title class="text-xl">
+      <span>产品分类管理</span>
+      <a href="/manger/product" class="px-10 h-full ml-5 rounded bg-blue-400 text-white cursor-pointer hover:text-orange-500 ">产品中心</a>
+    </rounded-title>
+  </div>
+</template>
+
+<style scoped>
+
+</style>

+ 31 - 0
server/control/c_solution.js

@@ -2,6 +2,7 @@ const {searchHandle} = require('../tools/searchSql');
 const {handle} = require('../tools/handle_cjs');
 const d_solution = require("../database/d_solution");
 const codeMap = require("../map/rcodeMap");
+const {isEmpty} = require("../tools/typeTool_cjs");
 const log = require("../logger").logger("c_solution","info")
 
 /**
@@ -89,10 +90,40 @@ async function searchNews(type, key, p, l)
     l);
 }
 
+async function searchAllNews(pType,type, key, p, l){
+  p = p || 1;
+  l = l || 10;
+  let _params = { }
+  if(pType !== 'all'){
+    _params.pType = type;
+  }
+  if(type !== 'all'){
+    _params.type = type;
+  }
+  if(key){
+    _params.key = key
+  }
+
+  if(isEmpty(_params)){
+    return [
+      {
+        eCode:codeMap.NotParam,
+        eMsg:`搜索文章参数异常`
+      },null]
+  }
+  return await searchHandle(
+    '搜索文章失败',
+    d_solution.searchSolution,
+    _params,
+    p,
+    l);
+}
+
 module.exports = {
   loadSolution,
   searchSolution,
   searchNews,
+  searchAllNews,
   getSolutionInfo
 }
 

+ 6 - 1
server/database/d_solution.js

@@ -49,10 +49,15 @@ function searchSolution(type='array',searchParam,page,limit){
    hfy_news_type as n_type
   `
   sql += ` where news.type_id = n_type.type_id`
-  sql += ` and n_type.parent_type = ${searchParam.parentType}`
+  if(searchParam.parentType) {
+    sql += ` and n_type.parent_type = ?`
+    values.push(searchParam.parentType);
+  }
+
   if(searchParam.key){
     sql += ` and news.title like '%${searchParam.key}%'`
   }
+
   if(searchParam.type){
     sql += ` and n_type.type_key = ?`
     values.push(searchParam.type)

+ 29 - 0
server/router/r_news.js

@@ -3,6 +3,7 @@ const {paramFail, ServerError, success, controlError, searchSuccess} = require("
 const c_solution = require("../control/c_solution");
 const typeTool = require("../tools/typeTool_cjs");
 const c = require("../control/c_news");
+const {isEmpty} = require("../tools/typeTool_cjs");
 const log = require("../logger").logger("r_news","info");
 
 router.get(
@@ -30,6 +31,34 @@ router.get(
     }
 });
 
+router.get(
+  '/pages',
+  async (req, res) => {
+    try{
+      log.info(`获取所有文章接口`)
+      let err, result;
+      let {key, l, p, type ,pType} = req.query;
+      type = type || 'all';
+      pType = pType || 'all';
+      l = typeTool.toNumber(l);
+      p = typeTool.toNumber(p);
+      log.info(`page=${p},limit=${l}`);
+      [err, result] = await c_solution.searchAllNews(pType, type, key, p, l);
+      if(err){
+        return controlError(res, err, null);}
+      log.info(`result len=${result.arr.length}`)
+      searchSuccess(res,
+        result.arr,
+        result.total,
+        result.page,
+        result.limit,
+      );
+    }catch (e) {
+      ServerError(res, null, e.message);
+    }
+  });
+
+
 router.get('/mini', async (req, res) => {
   try{
     log.info(`[搜索文章] 只获取基础值`)

+ 30 - 0
until/copy.js

@@ -0,0 +1,30 @@
+
+
+export default function (content){
+    return new Promise((resolve,reject)=>{
+        // 判断是否支持 clipboard api 复制
+        try {
+            if (navigator.clipboard) {
+                navigator.clipboard.writeText(content).then(resolve).catch(reject);
+            } else {
+                let textarea = document.createElement('textarea');
+                // 隐藏此输入框
+                textarea.style.position = 'fixed';
+                textarea.style.opacity = '0';
+                textarea.style.top = '20px';
+                document.body.appendChild(textarea);
+                // 赋值
+                textarea.value = content;
+                // 选中
+                textarea.select();
+                // 复制
+                document.execCommand('copy', true);
+                // 移除输入框
+                document.body.removeChild(textarea);
+            }
+            resolve('ok');
+        } catch (error) {
+            reject(error);
+        }
+    })
+}