Bladeren bron

初步完工

shaogen1995 10 maanden geleden
bovenliggende
commit
142645bf68

+ 2 - 2
src/components/ZupOne/index.tsx

@@ -27,7 +27,7 @@ type Props = {
   size: number //上传附件大小(M)
   dirCode: string //文件的code码
   myUrl: string //请求地址
-  format: string[] //上传格式 ["image/jpeg", "image/png"] ["video/mp4"] ,application/pdf
+  format: string[] //上传格式 ["image/jpeg", "image/png"] ["video/mp4"] ,application/pdf ,'audio/mpeg'
   formatTxt: string //上传图片提示
   checkTxt: string
   upTxt: string
@@ -66,7 +66,7 @@ function ZupOne(
       if (e.target.files) {
         // 拿到files信息
         const filesInfo = e.target.files[0]
-        // console.log("-----", filesInfo.type);
+        console.log('-----', filesInfo.type)
 
         // 校验格式
         const type = format

+ 3 - 3
src/pages/A4video/A4add/index.tsx

@@ -138,7 +138,7 @@ function A4add({ editInfo, closeFu, addTableFu, editTableFu }: Props) {
                 isLook={editInfo.txt === '查看'}
                 fileCheck={fileCheck}
                 size={5}
-                dirCode={'A2video'}
+                dirCode={'A4video'}
                 myUrl='cms/video/upload'
                 format={['image/jpeg', 'image/png']}
                 formatTxt='png、jpg和jpeg'
@@ -152,7 +152,7 @@ function A4add({ editInfo, closeFu, addTableFu, editTableFu }: Props) {
 
           {/* 附件 */}
           <div className='formRow'>
-            <div className='formLeft'>
+            <div className='formLeft' style={{ marginTop: 3 }}>
               <span>* </span>
               附件:
             </div>
@@ -162,7 +162,7 @@ function A4add({ editInfo, closeFu, addTableFu, editTableFu }: Props) {
                 isLook={editInfo.txt === '查看'}
                 fileCheck={fileCheck}
                 size={200}
-                dirCode='A2video'
+                dirCode='A4video'
                 myUrl='cms/video/upload'
                 format={['video/mp4']}
                 formatTxt='mp4'

+ 104 - 0
src/pages/A5story/A5add/index.module.scss

@@ -0,0 +1,104 @@
+.A5add {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 12;
+  background-color: #fff;
+  border-radius: 10px;
+  padding: 24px;
+
+  :global {
+    .A5aMain {
+      width: 100%;
+      height: 100%;
+      overflow-y: auto;
+
+      textarea {
+        min-height: 300px !important;
+      }
+
+      .A5fromRow {
+        position: relative;
+        width: 800px;
+
+        .A5_6Frow {
+          position: absolute;
+          left: 200px;
+          top: 5px;
+          color: #999;
+          font-size: 12px;
+        }
+      }
+
+      .ant-form {
+        width: 800px;
+
+        // .ant-input-affix-wrapper{
+        //   width: 800px;
+        // }
+        .formRow {
+          display: flex;
+
+          .formLeft {
+            position: relative;
+            width: 100px;
+            text-align: right;
+
+            & > span {
+              color: #ff4d4f;
+            }
+          }
+
+          .formRight {
+            width: calc(100% - 100px);
+          }
+        }
+
+        .formRow2 {
+          margin-top: -28px;
+          .formLeft {
+            margin-top: 20px;
+          }
+        }
+
+        .A5abtn {
+          position: absolute;
+          z-index: 10;
+          left: 1200px;
+          top: 50%;
+          transform: translateY(-50%);
+        }
+      }
+    }
+
+    // 从查看进入
+    .A5aMainLook {
+      // 左边的 label 也不让选中
+      label {
+        pointer-events: none;
+      }
+
+      .ant-picker {
+        pointer-events: none;
+      }
+
+      .ant-checkbox-wrapper {
+        pointer-events: none;
+      }
+
+      .ant-input-number {
+        pointer-events: none;
+      }
+
+      .ant-select {
+        pointer-events: none;
+      }
+
+      .ant-radio-wrapper {
+        pointer-events: none;
+      }
+    }
+  }
+}

+ 244 - 0
src/pages/A5story/A5add/index.tsx

@@ -0,0 +1,244 @@
+import React, { useCallback, useEffect, useRef, useState } from 'react'
+import styles from './index.module.scss'
+import { A1EditInfoType } from '@/pages/A1goods/data'
+import { Button, DatePicker, Form, FormInstance, Input, InputNumber } from 'antd'
+import { MessageFu } from '@/utils/message'
+import classNames from 'classnames'
+import ZupOne from '@/components/ZupOne'
+import TextArea from 'antd/es/input/TextArea'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import { A5_APIgetInfo, A5_APIsave } from '@/store/action/A5story'
+import dayjs from 'dayjs'
+import { A5topType } from '../data'
+import ZupTypes from '@/components/ZupTypes'
+
+type Props = {
+  editInfo: A1EditInfoType
+  closeFu: () => void
+  addTableFu: () => void
+  editTableFu: () => void
+  type: A5topType
+}
+
+function A5add({ editInfo, closeFu, addTableFu, editTableFu, type }: Props) {
+  // 表单的ref
+  const FormBoxRef = useRef<FormInstance>(null)
+
+  // 多张图片的ref
+  const ZupImgsRef = useRef<any>(null)
+
+  // 附件的ref
+  const ZupAudioRef = useRef<any>(null)
+
+  // 编辑/查看 进入页面 获取信息
+  const getInfoFu = useCallback(async (id: number) => {
+    const res = await A5_APIgetInfo(id)
+    if (res.code === 0) {
+      const info = res.data.entity
+      const obj = {
+        ...info,
+        myTime: dayjs(info.publishDay)
+      }
+
+      FormBoxRef.current?.setFieldsValue(obj)
+
+      // 设置附件
+      if (info.filePath) {
+        ZupAudioRef.current?.setFileComFileFu({
+          fileName: info.fileName,
+          filePath: info.filePath
+        })
+      }
+
+      const file = res.data.file || []
+
+      // 传给 附件 组件的
+      const sonInfo = {
+        type: 'img',
+        fileList: file
+      }
+      ZupImgsRef.current?.setFileComFileFu(sonInfo)
+    }
+  }, [])
+
+  // 附件 是否 已经点击过确定
+  const [fileCheck, setFileCheck] = useState(false)
+
+  useEffect(() => {
+    if (editInfo.id > 0) {
+      getInfoFu(editInfo.id)
+    } else {
+      FormBoxRef.current?.setFieldsValue({
+        sort: 999,
+        myTime: dayjs(Date.now())
+      })
+    }
+  }, [editInfo.id, getInfoFu])
+
+  // 没有通过校验
+  const onFinishFailed = useCallback(() => {
+    setFileCheck(true)
+  }, [])
+
+  //  通过校验点击确定
+  const onFinish = useCallback(
+    async (values: any) => {
+      setFileCheck(true)
+
+      // 没有传视频附件
+      let fileName = ''
+      let filePath = ''
+      // txt附件
+      const ZupAudioRefObj = ZupAudioRef.current?.fileComFileResFu()
+      fileName = ZupAudioRefObj.fileName
+      filePath = ZupAudioRefObj.filePath
+      // if (!filePath) return MessageFu.warning('请上传附件!')
+
+      // 附件组件的 type 数组 和 附件id数组
+      const { sonFileIds, sonIsOk } = ZupImgsRef.current?.fileComFileResFu()
+      if (sonIsOk) return
+
+      // 发布日期
+      const publishDay = dayjs(values.myTime).format('YYYY-MM-DD')
+
+      const obj = {
+        ...values,
+        id: editInfo.id > 0 ? editInfo.id : null,
+        fileName,
+        filePath,
+        publishDay,
+        type,
+        fileIds: sonFileIds ? sonFileIds.join(',') : null
+      }
+
+      const res = await A5_APIsave(obj)
+
+      if (res.code === 0) {
+        MessageFu.success(editInfo.txt + '成功!')
+        editInfo.id > 0 ? editTableFu() : addTableFu()
+        closeFu()
+      }
+    },
+    [addTableFu, closeFu, editInfo.id, editInfo.txt, editTableFu, type]
+  )
+
+  return (
+    <div className={styles.A5add}>
+      <div className={classNames('A5aMain', editInfo.txt === '查看' ? 'A5aMainLook' : '')}>
+        <Form
+          ref={FormBoxRef}
+          name='basic'
+          labelCol={{ span: 3 }}
+          onFinish={onFinish}
+          onFinishFailed={onFinishFailed}
+          autoComplete='off'
+          scrollToFirstError
+        >
+          <Form.Item label='标题' name='name' rules={[{ required: true, message: '请输入标题!' }]}>
+            <Input
+              readOnly={editInfo.txt === '查看'}
+              placeholder='请输入内容'
+              maxLength={20}
+              showCount
+            />
+          </Form.Item>
+
+          <Form.Item
+            label='发布日期'
+            name='myTime'
+            rules={[{ required: true, message: '请选择发布日期!' }]}
+          >
+            <DatePicker />
+          </Form.Item>
+
+          {/* 音频 */}
+          <div className='formRow'>
+            <div className='formLeft' style={{ marginTop: 3 }}>
+              音频:
+            </div>
+            <div className='formRight'>
+              <ZupOne
+                ref={ZupAudioRef}
+                isLook={editInfo.txt === '查看'}
+                fileCheck={false}
+                size={20}
+                dirCode='A5story'
+                myUrl='cms/audio/upload'
+                format={['audio/mpeg']}
+                formatTxt='mp3'
+                checkTxt='请上传mp3附件!'
+                upTxt='最多1个'
+                myType='audio'
+              />
+            </div>
+          </div>
+          {editInfo.txt === '查看' ? <br /> : null}
+
+          {/* 图片 */}
+          <div className='formRow formRow2'>
+            <div className='formLeft'>
+              <span>* </span>
+              图片:
+            </div>
+            <div className='formRight'>
+              <ZupTypes
+                ref={ZupImgsRef}
+                isLook={editInfo.txt === '查看'}
+                fileCheck={fileCheck}
+                selecFlag='图片'
+                imgSize={5}
+                dirCode={'A5story'}
+                myUrl='cms/audio/upload'
+                isTypeShow={true}
+                imgLength={20}
+              />
+            </div>
+          </div>
+          {editInfo.txt === '查看' ? <br /> : null}
+
+          <Form.Item label='正文' name='remark'>
+            <TextArea
+              readOnly={editInfo.txt === '查看'}
+              maxLength={1000}
+              showCount
+              placeholder='请输入内容'
+            />
+          </Form.Item>
+
+          <div className='A5fromRow'>
+            <Form.Item
+              label='排序值'
+              name='sort'
+              rules={[{ required: true, message: '请输入排序值!' }]}
+            >
+              <InputNumber min={1} max={999} precision={0} placeholder='请输入' />
+            </Form.Item>
+            <div className='A5_6Frow' hidden={editInfo.txt === '查看'}>
+              请输入1~999的数字。数字越小,排序越靠前。数字相同时,更新发布的内容排在前面
+            </div>
+          </div>
+
+          {/* 确定和取消按钮 */}
+          <Form.Item className='A5abtn'>
+            {editInfo.txt === '查看' ? (
+              <Button onClick={closeFu}>返回</Button>
+            ) : (
+              <>
+                <Button type='primary' htmlType='submit'>
+                  提交
+                </Button>
+                <br />
+                <br />
+                <MyPopconfirm txtK='取消' onConfirm={closeFu} />
+              </>
+            )}
+          </Form.Item>
+        </Form>
+      </div>
+    </div>
+  )
+}
+
+const MemoA5add = React.memo(A5add)
+
+export default MemoA5add

+ 9 - 0
src/pages/A5story/data.ts

@@ -0,0 +1,9 @@
+export type A5FromDataType = {
+  searchKey: string
+  type: A5topType
+
+  pageNum: number
+  pageSize: number
+}
+
+export type A5topType = 1 | 2

+ 26 - 0
src/pages/A5story/index.module.scss

@@ -1,4 +1,30 @@
 .A5story {
+  position: relative;
   :global {
+    .A5top {
+      border-radius: 10px;
+      background-color: #fff;
+      padding: 15px 24px;
+      display: flex;
+      justify-content: space-between;
+      & > div {
+        display: flex;
+        .A5TopRow {
+          display: flex;
+          align-items: center;
+          margin-right: 20px;
+          .ant-select-selection-placeholder {
+            color: black;
+          }
+        }
+      }
+    }
+    .A5tableBox {
+      border-radius: 10px;
+      overflow: hidden;
+      margin-top: 15px;
+      height: calc(100% - 77px);
+      background-color: #fff;
+    }
   }
 }

+ 163 - 2
src/pages/A5story/index.tsx

@@ -1,9 +1,170 @@
-import React from 'react'
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
 import styles from './index.module.scss'
+import { useDispatch, useSelector } from 'react-redux'
+import { RootState } from '@/store'
+import { MessageFu } from '@/utils/message'
+import { A1tableType } from '@/types'
+import { Button, Input } from 'antd'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import MyTable from '@/components/MyTable'
+import { A5tableC } from '@/utils/tableData'
+import { A1EditInfoType } from '../A1goods/data'
+import { A5_APIdel, A5_APIgetList } from '@/store/action/A5story'
+import { A5FromDataType, A5topType } from './data'
+import A5add from './A5add'
+
+const topTypeArr: { id: A5topType; name: string }[] = [
+  {
+    id: 1,
+    name: '赏文物 听故事'
+  },
+  {
+    id: 2,
+    name: '花明楼 赏文物'
+  }
+]
+
+const fromDataBase: A5FromDataType = {
+  searchKey: '',
+  type: 1,
+  pageNum: 1,
+  pageSize: 10
+}
+
 function A5story() {
+  const dispatch = useDispatch()
+
+  const [fromData, setFromData] = useState(fromDataBase)
+
+  const getListFu = useCallback(() => {
+    dispatch(A5_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') => {
+      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 tableInfo = useSelector((state: RootState) => state.A5story.tableInfo)
+
+  const delTableFu = useCallback(
+    async (id: number) => {
+      const res = await A5_APIdel(id)
+      if (res.code === 0) {
+        MessageFu.success('删除成功!')
+        getListFu()
+      }
+    },
+    [getListFu]
+  )
+
+  const tableLastBtn = useMemo(() => {
+    return [
+      {
+        title: '操作',
+        render: (item: A1tableType) => (
+          <>
+            <Button
+              size='small'
+              type='text'
+              onClick={() => setEditInfo({ id: item.id, txt: '编辑' })}
+            >
+              编辑
+            </Button>
+
+            <MyPopconfirm txtK='删除' onConfirm={() => delTableFu(item.id)} />
+          </>
+        )
+      }
+    ]
+  }, [delTableFu])
+
+  //查看、新增、编辑
+  const [editInfo, setEditInfo] = useState<A1EditInfoType>({
+    id: 0,
+    txt: ''
+  })
+
   return (
     <div className={styles.A5story}>
-      <h1>A5story</h1>
+      <div className='pageTitle'>文物故事 {editInfo.id ? ` - ${editInfo.txt}` : ''}</div>
+
+      {/* 顶部筛选 */}
+      <div className='A5top'>
+        <div>
+          <div className='A5TopRow'>
+            {topTypeArr.map(v => (
+              <Button
+                onClick={() => setFromData({ ...fromData, type: v.id })}
+                key={v.id}
+                type={v.id === fromData.type ? 'primary' : 'default'}
+              >
+                {v.name}
+              </Button>
+            ))}
+          </div>
+          <div className='A5TopRow'>
+            <span>搜索:</span>
+            <Input
+              key={inputKey}
+              maxLength={20}
+              style={{ width: 200 }}
+              placeholder='请输入名称'
+              allowClear
+              onChange={e => txtChangeFu(e, 'searchKey')}
+            />
+          </div>
+        </div>
+        <div>
+          <Button onClick={resetSelectFu}>重置</Button>&emsp;
+          <Button type='primary' onClick={() => setEditInfo({ id: -1, txt: '新增' })}>
+            新增
+          </Button>
+        </div>
+      </div>
+
+      {/* 表格主体 */}
+      <div className='A5tableBox'>
+        <MyTable
+          yHeight={625}
+          list={tableInfo.list}
+          columnsTemp={A5tableC}
+          lastBtn={tableLastBtn}
+          pageNum={fromData.pageNum}
+          pageSize={fromData.pageSize}
+          total={tableInfo.total}
+          onChange={(pageNum, pageSize) => setFromData({ ...fromData, pageNum, pageSize })}
+        />
+      </div>
+
+      {editInfo.id ? (
+        <A5add
+          editInfo={editInfo}
+          closeFu={() => setEditInfo({} as A1EditInfoType)}
+          addTableFu={resetSelectFu}
+          editTableFu={getListFu}
+          type={fromData.type}
+        />
+      ) : null}
     </div>
   )
 }

+ 39 - 0
src/store/action/A5story.ts

@@ -0,0 +1,39 @@
+import http from '@/utils/http'
+import { AppDispatch } from '..'
+
+/**
+ *文物故事-列表
+ */
+
+export const A5_APIgetList = (data: any): any => {
+  return async (dispatch: AppDispatch) => {
+    const res = await http.post('cms/audio/pageList', data)
+    if (res.code === 0) {
+      const obj = {
+        list: res.data.records,
+        total: res.data.total
+      }
+      dispatch({ type: 'A5/getList', payload: obj })
+    }
+  }
+}
+/**
+ * 文物故事-删除
+ */
+export const A5_APIdel = (id: number) => {
+  return http.get(`cms/audio/remove/${id}`)
+}
+
+/**
+ * 文物故事-获取详情
+ */
+export const A5_APIgetInfo = (id: number) => {
+  return http.get(`cms/audio/detail/${id}`)
+}
+
+/**
+ * 文物故事-新增、编辑
+ */
+export const A5_APIsave = (data: any) => {
+  return http.post('cms/audio/save', data)
+}

+ 28 - 0
src/store/reducer/A5story.ts

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

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

@@ -6,6 +6,7 @@ import A0Layout from './layout'
 import A1goods from './A1goods'
 import A2panorama from './A2panorama'
 import A4video from './A4video'
+import A5story from './A5story'
 import Z1user from './Z1user'
 import Z2log from './Z2log'
 
@@ -15,6 +16,7 @@ const rootReducer = combineReducers({
   A1goods,
   A2panorama,
   A4video,
+  A5story,
   Z1user,
   Z2log
 })

+ 13 - 1
src/types/api/A4video.ts

@@ -1 +1,13 @@
-export type A4tableType = any
+export type A4tableType = {
+  createTime: string
+  creatorId: number
+  creatorName: string
+  fileName: string
+  filePath: string
+  id: number
+  name: string
+  remark: string
+  sort: number
+  thumb: string
+  updateTime: string
+}

+ 2 - 0
src/types/api/A5story.ts

@@ -0,0 +1,2 @@
+export type A5tableType = any
+// 待完善

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

@@ -2,5 +2,6 @@ export * from './api/layot'
 export * from './api/A1goods'
 export * from './api/A2panorama'
 export * from './api/A4video'
+export * from './api/A5story'
 export * from './api/Z1user'
 export * from './api/Z2log'

+ 7 - 0
src/utils/tableData.ts

@@ -36,6 +36,13 @@ export const A4tableC = [
   ['txt', '排序值', 'sort']
 ]
 
+export const A5tableC = [
+  ['txt', '标题', 'name'],
+  ['text', '正文', 'remark', 100],
+  ['txt', '发布日期', 'publishDay'],
+  ['txt', '排序值', 'sort']
+]
+
 export const Z1tableC = [
   ['txt', '用户名', 'userName'],
   ['txtChange', '角色', 'isAdmin', { 1: '管理员', 0: '普通成员' }],