Browse Source

图书推荐

shaogen1995 10 months ago
parent
commit
34a8ce88f4

+ 1 - 1
src/pages/A1manage/A1ying/A1Yadd.tsx

@@ -108,7 +108,7 @@ function A1Yadd({ type, id, closeFu, addTableFu, editTableFu, bookId }: Props) {
         closeFu()
         closeFu()
       }
       }
     },
     },
-    [addTableFu, closeFu, editTableFu, id, type]
+    [addTableFu, bookId, closeFu, editTableFu, id, type]
   )
   )
 
 
   return (
   return (

+ 5 - 2
src/pages/A1manage/A1ying/index.tsx

@@ -21,10 +21,11 @@ type FromDataType = {
 type Props = {
 type Props = {
   bookId: number
   bookId: number
   type: 'img' | 'video'
   type: 'img' | 'video'
+  name: string
   closeFu: () => void
   closeFu: () => void
 }
 }
 
 
-function A1ying({ bookId, type, closeFu }: Props) {
+function A1ying({ bookId, type, closeFu, name }: Props) {
   const [fromData, setFromData] = useState({} as FromDataType)
   const [fromData, setFromData] = useState({} as FromDataType)
 
 
   useEffect(() => {
   useEffect(() => {
@@ -121,7 +122,9 @@ function A1ying({ bookId, type, closeFu }: Props) {
       <div className='A1Ytop'>
       <div className='A1Ytop'>
         <div className='A1YtopRow'>
         <div className='A1YtopRow'>
           <ArrowLeftOutlined onClick={closeFu} />
           <ArrowLeftOutlined onClick={closeFu} />
-          <div className='A1YtopBack'>{type === 'img' ? '影印资料' : '视频资料'}</div>
+          <div className='A1YtopBack'>
+            {name} - {type === 'img' ? '影印资料' : '视频资料'}
+          </div>
           <span>搜索:</span>
           <span>搜索:</span>
           <Input
           <Input
             key={inputKey}
             key={inputKey}

+ 18 - 11
src/pages/A1manage/index.tsx

@@ -55,6 +55,7 @@ function A1manage() {
   useEffect(() => {
   useEffect(() => {
     getListFu()
     getListFu()
   }, [getListFu])
   }, [getListFu])
+
   const [inputKey, setInputKey] = useState(1)
   const [inputKey, setInputKey] = useState(1)
 
 
   // 输入框的输入
   // 输入框的输入
@@ -111,14 +112,14 @@ function A1manage() {
             <Button
             <Button
               size='small'
               size='small'
               type='text'
               type='text'
-              onClick={() => setBookYing({ bookId: item.id, type: 'img' })}
+              onClick={() => setBookYing({ bookId: item.id, type: 'img', name: item.name })}
             >
             >
               影印
               影印
             </Button>
             </Button>
             <Button
             <Button
               size='small'
               size='small'
               type='text'
               type='text'
-              onClick={() => setBookYing({ bookId: item.id, type: 'video' })}
+              onClick={() => setBookYing({ bookId: item.id, type: 'video', name: item.name })}
             >
             >
               视频
               视频
             </Button>
             </Button>
@@ -137,10 +138,13 @@ function A1manage() {
   })
   })
 
 
   // 影印和视频
   // 影印和视频
-  const [bookYing, setBookYing] = useState<{ bookId: number; type: 'img' | 'video' }>({
-    bookId: 0,
-    type: 'img'
-  })
+  const [bookYing, setBookYing] = useState<{ bookId: number; type: 'img' | 'video'; name: string }>(
+    {
+      bookId: 0,
+      type: 'img',
+      name: ''
+    }
+  )
 
 
   return (
   return (
     <div className={styles.A1manage}>
     <div className={styles.A1manage}>
@@ -154,7 +158,7 @@ function A1manage() {
             <Input
             <Input
               key={inputKey}
               key={inputKey}
               maxLength={20}
               maxLength={20}
-              style={{ width: 200 }}
+              style={{ width: 192 }}
               placeholder='请输入书名/作者/出版社'
               placeholder='请输入书名/作者/出版社'
               allowClear
               allowClear
               onChange={e => txtChangeFu(e, 'searchKey')}
               onChange={e => txtChangeFu(e, 'searchKey')}
@@ -163,7 +167,7 @@ function A1manage() {
             <Input
             <Input
               key={inputKey + 1}
               key={inputKey + 1}
               maxLength={20}
               maxLength={20}
-              style={{ width: 200 }}
+              style={{ width: 192 }}
               placeholder='请输入完整ISBN编号'
               placeholder='请输入完整ISBN编号'
               allowClear
               allowClear
               onChange={e => txtChangeFu(e, 'num')}
               onChange={e => txtChangeFu(e, 'num')}
@@ -174,7 +178,7 @@ function A1manage() {
             <span>中图法分类:</span>
             <span>中图法分类:</span>
             <Cascader
             <Cascader
               changeOnSelect
               changeOnSelect
-              style={{ width: 300 }}
+              style={{ width: 250 }}
               options={storageArr}
               options={storageArr}
               fieldNames={{ label: 'name', value: 'id', children: 'children' }}
               fieldNames={{ label: 'name', value: 'id', children: 'children' }}
               value={fromData.storageArr}
               value={fromData.storageArr}
@@ -192,7 +196,7 @@ function A1manage() {
             <Select
             <Select
               allowClear
               allowClear
               placeholder='全部'
               placeholder='全部'
-              style={{ width: 200 }}
+              style={{ width: 180 }}
               value={fromData.exhibitTypeId}
               value={fromData.exhibitTypeId}
               fieldNames={{ label: 'name', value: 'id' }}
               fieldNames={{ label: 'name', value: 'id' }}
               onChange={e => setFromData({ ...fromData, pageNum: 1, exhibitTypeId: e })}
               onChange={e => setFromData({ ...fromData, pageNum: 1, exhibitTypeId: e })}
@@ -201,6 +205,8 @@ function A1manage() {
           </div>
           </div>
         </div>
         </div>
         <div>
         <div>
+          {/* 待完善 */}
+          <Button>批量导入</Button>&emsp;
           <Button onClick={resetSelectFu}>重置</Button>&emsp;
           <Button onClick={resetSelectFu}>重置</Button>&emsp;
           <Button type='primary' onClick={() => setEditInfo({ id: -1, txt: '新增' })}>
           <Button type='primary' onClick={() => setEditInfo({ id: -1, txt: '新增' })}>
             新增
             新增
@@ -239,7 +245,8 @@ function A1manage() {
         <A1ying
         <A1ying
           bookId={bookYing.bookId}
           bookId={bookYing.bookId}
           type={bookYing.type}
           type={bookYing.type}
-          closeFu={() => setBookYing({ bookId: 0, type: 'img' })}
+          name={bookYing.name}
+          closeFu={() => setBookYing({ bookId: 0, type: 'img', name: '' })}
         />
         />
       ) : null}
       ) : null}
     </div>
     </div>

+ 163 - 0
src/pages/A3recommend/A3add.tsx

@@ -0,0 +1,163 @@
+import React, { useCallback, useEffect, useState } from 'react'
+import styles from './index.module.scss'
+import { Button, InputNumber, Modal, Select } from 'antd'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import { A1_APIgetSelect, A3_APIgetInfo, A3_APIsave } from '@/store/action/A3recommend'
+import { MessageFu } from '@/utils/message'
+
+type Props = {
+  type: 'index' | 'rank'
+  id: number
+  closeFu: () => void
+  addTableFu: () => void
+}
+
+function A3add({ type, id, closeFu, addTableFu }: Props) {
+  const getSelect = useCallback(async () => {
+    const res = await A1_APIgetSelect()
+    if (res.code === 0) {
+      setSelectArr(res.data.records.map((v: any) => ({ value: v.id, name: v.name, num: v.num })))
+    }
+  }, [])
+
+  useEffect(() => {
+    getSelect()
+  }, [getSelect])
+
+  const getInfo = useCallback(async (id: number) => {
+    const res = await A3_APIgetInfo(id)
+    if (res.code === 0) {
+      setName(res.data.bookId)
+      if (res.data.num) setNum(res.data.bookId)
+
+      setSort(res.data.sort)
+    }
+  }, [])
+
+  useEffect(() => {
+    if (id > 0) getInfo(id)
+  }, [getInfo, id])
+
+  const [selectArr, setSelectArr] = useState<{ value: number; name: string; num: string }[]>([])
+
+  const [name, setName] = useState<number | null>(null)
+  const [num, setNum] = useState<number | null>(null)
+
+  const [sort, setSort] = useState<number>(999)
+
+  const onChange = useCallback(
+    (value: number) => {
+      setName(value)
+
+      const obj = selectArr.find(v => v.value === value)!
+
+      if (obj.num) setNum(value)
+      else setNum(null)
+    },
+    [selectArr]
+  )
+
+  // 拿来占位,不然会报错
+  const onSearch = useCallback((value: string) => {
+    // console.log(`search ${value}`)
+  }, [])
+
+  const btnOk = useCallback(async () => {
+    if (!Number(name)) return MessageFu.warning('请输入书名!')
+    if (!sort) return MessageFu.warning('请输入排序值!')
+
+    const obj = {
+      id: id > 0 ? id : null,
+      type,
+      bookId: Number(name),
+      sort
+    }
+
+    const res = await A3_APIsave(obj)
+    if (res.code === 0) {
+      MessageFu.success(id > 0 ? '编辑成功!' : '新增成功!')
+      addTableFu()
+      closeFu()
+    }
+  }, [addTableFu, closeFu, id, name, sort, type])
+
+  return (
+    <Modal
+      wrapClassName={styles.A3add}
+      open={true}
+      title={id > 0 ? '编辑' : '新增'}
+      footer={
+        [] // 设置footer为空,去掉 取消 确定默认按钮
+      }
+    >
+      <div className='formRow'>
+        <div className='formLeft'>
+          <span>*</span>书名:
+        </div>
+        <div className='formRight'>
+          <Select
+            showSearch
+            value={name}
+            maxLength={20}
+            placeholder={'请输入内容模糊搜索'}
+            style={{ width: 400 }}
+            onChange={onChange}
+            onSearch={onSearch}
+            fieldNames={{ label: 'name', value: 'value' }}
+            optionFilterProp='name'
+            options={selectArr}
+          />
+        </div>
+      </div>
+
+      <div className='formRow'>
+        <div className='formLeft'>ISBN编号:</div>
+        <div className='formRight'>
+          <Select
+            showSearch
+            value={num}
+            maxLength={20}
+            placeholder={'请输入内容模糊搜索'}
+            style={{ width: 400 }}
+            onChange={onChange}
+            onSearch={onSearch}
+            fieldNames={{ label: 'num', value: 'value' }}
+            optionFilterProp='num'
+            options={selectArr.filter(v => v.num)}
+          />
+        </div>
+      </div>
+
+      <div className='formRow'>
+        <div className='formLeft'>
+          <span>*</span>排序值:
+        </div>
+        <div className='formRight'>
+          <InputNumber
+            value={sort}
+            onChange={e => setSort(e ? e : 1)}
+            min={1}
+            max={999}
+            precision={0}
+            placeholder='请输入'
+          />
+          <div className='formRightTit'>
+            请输入1~999的数字。数字越小,排序越靠前。数字相同时,更新发布的内容排在前面
+          </div>
+        </div>
+      </div>
+
+      <div className='A3abtn'>
+        <Button type='primary' onClick={btnOk}>
+          提交
+        </Button>
+        &emsp;
+        <MyPopconfirm txtK='取消' onConfirm={closeFu} />
+      </div>
+    </Modal>
+  )
+}
+
+const MemoA3add = React.memo(A3add)
+
+export default MemoA3add

+ 7 - 0
src/pages/A3recommend/data.ts

@@ -0,0 +1,7 @@
+export type A3FromDataType = {
+  type: 'index' | 'rank'
+  searchKey: string
+  num: string
+  pageNum: number
+  pageSize: number
+}

+ 75 - 2
src/pages/A3recommend/index.module.scss

@@ -1,5 +1,78 @@
-.A3recommend{
-  :global{
+.A3recommend {
+  position: relative;
+  :global {
+    .A3top {
+      border-radius: 10px;
+      background-color: #fff;
+      padding: 15px 24px;
+      display: flex;
+      justify-content: space-between;
+      & > div {
+        display: flex;
+        .A3TopRow {
+          display: flex;
+          align-items: center;
+          margin-right: 20px;
+          .ant-select-selection-placeholder {
+            color: black;
+          }
+        }
+      }
+    }
+    .A3tableBox {
+      border-radius: 10px;
+      overflow: hidden;
+      margin-top: 15px;
+      height: calc(100% - 77px);
+      background-color: #fff;
+    }
+  }
+}
+
+// 新增 、编辑弹窗
+.A3add {
+  :global {
+    .ant-modal-close {
+      display: none;
+    }
+    .ant-modal {
+      width: 800px !important;
+    }
+    .ant-modal-body {
+      border-top: 1px solid #ccc;
+      padding-top: 15px !important;
+    }
+
+    .formRow {
+      display: flex;
+      align-items: center;
+      margin-bottom: 24px;
+
+      .formLeft {
+        width: 80px;
+        text-align: right;
+
+        & > span {
+          color: #ff4d4f;
+        }
+      }
+
+      .formRight {
+        width: calc(100% - 80px);
+        position: relative;
+        .formRightTit {
+          position: absolute;
+          left: 110px;
+          top: 5px;
+          color: #999;
+          font-size: 12px;
+        }
+      }
+    }
 
 
+    .A3abtn {
+      text-align: center;
+      margin-top: 30px;
+    }
   }
   }
 }
 }

+ 173 - 6
src/pages/A3recommend/index.tsx

@@ -1,14 +1,181 @@
-import React from "react";
-import styles from "./index.module.scss";
- function A3recommend() {
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
+import styles from './index.module.scss'
+import { Button, Input } from 'antd'
+import { useDispatch, useSelector } from 'react-redux'
+import { A3FromDataType } from './data'
+import { A3_APIdel, A3_APIgetList } from '@/store/action/A3recommend'
+import { RootState } from '@/store'
+import { MessageFu } from '@/utils/message'
+import { A3tableType } from '@/types'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import MyTable from '@/components/MyTable'
+import { A3tableC } from '@/utils/tableData'
+import A3add from './A3add'
+
+const fromDataBase: A3FromDataType = {
+  type: 'index',
+  searchKey: '',
+  num: '',
+  pageNum: 1,
+  pageSize: 9999
+}
+
+const topTypeArr: { name: string; key: 'index' | 'rank' }[] = [
+  {
+    name: '首页推荐',
+    key: 'index'
+  },
+  {
+    name: '排行榜推荐',
+    key: 'rank'
+  }
+]
+
+function A3recommend() {
+  const dispatch = useDispatch()
+
+  const [fromData, setFromData] = useState(fromDataBase)
+
+  const getListFu = useCallback(() => {
+    dispatch(A3_APIgetList(fromData))
+  }, [dispatch, fromData])
+
+  useEffect(() => {
+    getListFu()
+  }, [getListFu])
+
+  const [inputKey, setInputKey] = useState(1)
+
+  // 输入框的输入
+  const timeRef = useRef(-1)
+  const txtChangeFu = useCallback(
+    (e: React.ChangeEvent<HTMLInputElement>, key: 'searchKey' | 'num') => {
+      clearTimeout(timeRef.current)
+      timeRef.current = window.setTimeout(() => {
+        setFromData({ ...fromData, [key]: e.target.value.replaceAll("'", ''), pageNum: 1 })
+      }, 500)
+    },
+    [fromData]
+  )
+
+  // 点击重置
+  const resetSelectFu = useCallback(() => {
+    setInputKey(Date.now())
+    setFromData({ ...fromDataBase, type: fromData.type })
+  }, [fromData.type])
+
+  const tableList = useSelector((state: RootState) => state.A3recommend.tableList)
+
+  const delTableFu = useCallback(
+    async (id: number) => {
+      const res = await A3_APIdel(id)
+      if (res.code === 0) {
+        MessageFu.success('删除成功!')
+        getListFu()
+      }
+    },
+    [getListFu]
+  )
+
+  const tableLastBtn = useMemo(() => {
+    return [
+      {
+        title: '操作',
+        render: (item: A3tableType) => (
+          <>
+            <Button size='small' type='text' onClick={() => setEditId(item.id)}>
+              编辑
+            </Button>
+
+            <MyPopconfirm txtK='删除' onConfirm={() => delTableFu(item.id)} />
+          </>
+        )
+      }
+    ]
+  }, [delTableFu])
+
+  // 点击编辑和新增
+  const [editId, setEditId] = useState(0)
 
 
   return (
   return (
     <div className={styles.A3recommend}>
     <div className={styles.A3recommend}>
-      <h1>A3recommend</h1>
+      <div className='pageTitle'>图书推荐</div>
+      {/* 顶部筛选 */}
+      <div className='A3top'>
+        <div>
+          <div className='A3TopRow'>
+            {topTypeArr.map(v => (
+              <Button
+                type={v.key === fromData.type ? 'primary' : 'default'}
+                key={v.key}
+                onClick={() => setFromData({ ...fromData, type: v.key, pageNum: 1 })}
+              >
+                {v.name}
+              </Button>
+            ))}
+          </div>
+
+          <div className='A3TopRow'>
+            <span>搜索:</span>
+            <Input
+              key={inputKey}
+              maxLength={20}
+              style={{ width: 192 }}
+              placeholder='请输入书名/作者/出版社'
+              allowClear
+              onChange={e => txtChangeFu(e, 'searchKey')}
+            />
+            &nbsp;
+            <Input
+              key={inputKey + 1}
+              maxLength={20}
+              style={{ width: 192 }}
+              placeholder='请输入完整ISBN编号'
+              allowClear
+              onChange={e => txtChangeFu(e, 'num')}
+            />
+          </div>
+        </div>
+        <div>
+          <Button onClick={resetSelectFu}>重置</Button>&emsp;
+          <Button
+            type='primary'
+            onClick={() => {
+              if (fromData.type === 'index' && tableList.length >= 30)
+                return MessageFu.warning('最多支持30本!')
+              if (fromData.type === 'rank' && tableList.length >= 10)
+                return MessageFu.warning('最多支持10本!')
+              setEditId(-1)
+            }}
+          >
+            新增
+          </Button>
+        </div>
+      </div>
+
+      {/* 表格主体 */}
+      <div className='A3tableBox'>
+        <MyTable
+          yHeight={678}
+          list={tableList}
+          columnsTemp={A3tableC}
+          lastBtn={tableLastBtn}
+          pagingInfo={false}
+        />
+      </div>
+
+      {editId ? (
+        <A3add
+          id={editId}
+          type={fromData.type}
+          closeFu={() => setEditId(0)}
+          addTableFu={resetSelectFu}
+        />
+      ) : null}
     </div>
     </div>
   )
   )
 }
 }
 
 
-const MemoA3recommend = React.memo(A3recommend);
+const MemoA3recommend = React.memo(A3recommend)
 
 
-export default MemoA3recommend;
+export default MemoA3recommend

+ 44 - 0
src/store/action/A3recommend.ts

@@ -0,0 +1,44 @@
+import http from '@/utils/http'
+import { AppDispatch } from '..'
+
+/**
+ *图书推荐-列表
+ */
+
+export const A3_APIgetList = (data: any): any => {
+  return async (dispatch: AppDispatch) => {
+    const res = await http.post('cms/rank/getList', data)
+    if (res.code === 0) {
+      dispatch({ type: 'A3/getList', payload: res.data })
+    }
+  }
+}
+
+// 获取新增 、编辑 里面的 书名/ISBN编号的下拉框数据
+export const A1_APIgetSelect = (): any => {
+  return http.post('cms/book/pageList', {
+    pageNum: 1,
+    pageSize: 99999
+  })
+}
+
+/**
+ * 图书推荐-删除
+ */
+export const A3_APIdel = (id: number) => {
+  return http.get(`cms/rank/remove/${id}`)
+}
+
+/**
+ * 图书推荐-获取详情
+ */
+export const A3_APIgetInfo = (id: number) => {
+  return http.get(`cms/rank/detail/${id}`)
+}
+
+/**
+ * 图书推荐-新增、编辑
+ */
+export const A3_APIsave = (data: any) => {
+  return http.post('cms/rank/save', data)
+}

+ 25 - 0
src/store/reducer/A3recommend.ts

@@ -0,0 +1,25 @@
+import { A3tableType } from '@/types'
+
+// 初始化状态
+const initState = {
+  // 列表数据
+  tableList: [] as A3tableType[]
+}
+
+// 定义 action 类型
+type Props = {
+  type: 'A3/getList'
+  payload: A3tableType[]
+}
+
+// reducer
+export default function Reducer(state = initState, action: Props) {
+  switch (action.type) {
+    // 获取列表数据
+    case 'A3/getList':
+      return { ...state, tableList: action.payload }
+
+    default:
+      return state
+  }
+}

+ 2 - 0
src/store/reducer/index.ts

@@ -5,6 +5,7 @@ import { combineReducers } from 'redux'
 import A0Layout from './layout'
 import A0Layout from './layout'
 import A1manage from './A1manage'
 import A1manage from './A1manage'
 import A2classify from './A2classify'
 import A2classify from './A2classify'
+import A3recommend from './A3recommend'
 import Z1user from './Z1user'
 import Z1user from './Z1user'
 import Z2log from './Z2log'
 import Z2log from './Z2log'
 
 
@@ -13,6 +14,7 @@ const rootReducer = combineReducers({
   A0Layout,
   A0Layout,
   A1manage,
   A1manage,
   A2classify,
   A2classify,
+  A3recommend,
   Z1user,
   Z1user,
   Z2log
   Z2log
 })
 })

+ 23 - 0
src/types/api/A3recommend.ts

@@ -0,0 +1,23 @@
+export type A3tableType = {
+  ancestor: string
+  author: string
+  createTime: string
+  creatorId: number
+  creatorName: string
+  description: string
+  exhibitTypeId: string
+  exhibitTypeName: string
+  fileName: string
+  filePath: string
+  id: number
+  name: string
+  num: string
+  pcsVisit: number
+  press: string
+  sort: number
+  storageId: string
+  storageName: string
+  thumb: string
+  updateTime: string
+  year: string
+}

+ 1 - 0
src/types/index.d.ts

@@ -1,5 +1,6 @@
 export * from './api/layot'
 export * from './api/layot'
 export * from './api/A1manage'
 export * from './api/A1manage'
 export * from './api/A2classify'
 export * from './api/A2classify'
+export * from './api/A3recommend'
 export * from './api/Z1user'
 export * from './api/Z1user'
 export * from './api/Z2log'
 export * from './api/Z2log'

+ 11 - 0
src/utils/tableData.ts

@@ -52,6 +52,17 @@ export const A2tableC = [
   ['txt', '编辑时间', 'updateTime']
   ['txt', '编辑时间', 'updateTime']
 ]
 ]
 
 
+export const A3tableC = [
+  ['txt', '书名', 'name'],
+  ['img', '封面', 'thumb'],
+  ['txt', '作者', 'author'],
+  ['txt', '出版社', 'press'],
+  ['txt', 'ISBN编号', 'num'],
+  ['txt', '排序值', 'sort'],
+  ['txt', '编辑人', 'creatorName'],
+  ['txt', '编辑时间', 'updateTime']
+]
+
 export const Z1tableC = [
 export const Z1tableC = [
   ['txt', '用户名', 'userName'],
   ['txt', '用户名', 'userName'],
   ['txtChange', '角色', 'isAdmin', { 1: '管理员', 0: '普通成员' }],
   ['txtChange', '角色', 'isAdmin', { 1: '管理员', 0: '普通成员' }],