liujiangyong
9 months ago
40 changed files with 1926 additions and 344 deletions
@ -0,0 +1,71 @@ |
|||
'use strict'; |
|||
|
|||
async function getLabels(ctx, next) { |
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
|
|||
const res = await models.QrcodeLabels.findAll({ |
|||
order: [['id', 'DESC']], |
|||
}) |
|||
|
|||
ctx.status = 200; |
|||
ctx.body = res |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { |
|||
"message": "获取标签失败" |
|||
} |
|||
} |
|||
} |
|||
|
|||
async function createLabels(ctx, next) { |
|||
const models = ctx.fs.dc.models; |
|||
try { |
|||
let rslt = ctx.request.body; |
|||
await models.Department.create(Object.assign({}, rslt, { read: 1 })) |
|||
ctx.status = 204; |
|||
ctx.body = { message: '新建部门成功' } |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { message: '新建部门失败' } |
|||
} |
|||
} |
|||
|
|||
async function delLabels(ctx, next) { |
|||
let errMsg = "删除部门失败"; |
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
const { id } = ctx.params; |
|||
let list = await models.Department.findAll({}); |
|||
let deptIds = list.map(l => l.id); |
|||
const allUsers = await models.User.findAll({ |
|||
where: { |
|||
departmentId: { $in: deptIds }, |
|||
delete: false |
|||
} |
|||
}) |
|||
const childrenDept = await models.Department.findAll({ where: { dependence: id } }) |
|||
const childrenUser = allUsers.filter(au => au.departmentId == id); |
|||
if (childrenUser.length || childrenDept.length) { |
|||
errMsg = '请先删除部门下的用户或子部门'; |
|||
throw errMsg; |
|||
} |
|||
await models.Department.destroy({ where: { id: id } }); |
|||
await models.Department.destroy({ where: { dependence: id } }); |
|||
ctx.status = 204; |
|||
ctx.body = { message: '删除部门成功' } |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { message: error } |
|||
} |
|||
} |
|||
|
|||
|
|||
module.exports = { |
|||
getLabels, |
|||
createLabels, |
|||
delLabels, |
|||
} |
@ -0,0 +1,173 @@ |
|||
'use strict'; |
|||
const Hex = require('crypto-js/enc-hex'); |
|||
const MD5 = require('crypto-js/md5'); |
|||
|
|||
async function getPublicityInfo(ctx, next) { |
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
const { limit, page, name, label } = ctx.query |
|||
|
|||
let findOption = { |
|||
where: {}, |
|||
order: [['time', 'DESC']], |
|||
include: [ |
|||
{ |
|||
model: models.Qrcode, |
|||
}, |
|||
{ |
|||
model: models.QrcodeLabels, |
|||
attributes: ["name"], |
|||
}, |
|||
{ |
|||
model: models.QrcodeFiles, |
|||
}, |
|||
], |
|||
distinct: true |
|||
} |
|||
if (limit) { |
|||
findOption.limit = Number(limit) |
|||
} |
|||
if (page && limit) { |
|||
findOption.offset = page * limit |
|||
} |
|||
// if (startTime && endTime) {
|
|||
// findOption.where.time = { $between: [startTime, endTime] };
|
|||
// }
|
|||
if (name) { |
|||
findOption.where.name = { $like: `%${name}%` } |
|||
} |
|||
|
|||
const res = await models.PublicityInfo.findAndCountAll(findOption) |
|||
|
|||
ctx.status = 200; |
|||
ctx.body = res |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { |
|||
"message": "获取宣传信息失败" |
|||
} |
|||
} |
|||
} |
|||
|
|||
async function createPublicityInfo(ctx, next) { |
|||
const models = ctx.fs.dc.models; |
|||
const transaction = await ctx.fs.dc.orm.transaction(); |
|||
try { |
|||
const body = ctx.request.body; |
|||
const { type, name, labels, qrcode, files } = body; |
|||
|
|||
const dbQrcode = await models.Qrcode.create({ |
|||
name, type, url: qrcode.url, key: qrcode.key, logo: qrcode.logo, |
|||
}, { transaction }); |
|||
|
|||
const dbPublicityInfo = await models.PublicityInfo.create({ |
|||
...body, |
|||
qrcodeId: dbQrcode.id, |
|||
time: new Date().getTime(), |
|||
}, { transaction }); |
|||
|
|||
if (labels && labels.length) { |
|||
let labelIds = [] |
|||
let createLabels = []; |
|||
for (const l of labels) { |
|||
if (typeof l === 'string') { |
|||
createLabels.push({ name: l }); |
|||
} else { |
|||
labelIds.push(l); |
|||
} |
|||
} |
|||
if (createLabels.length) { |
|||
const dbLabels = await models.QrcodeLabels.bulkCreate(createLabels, { returning: true, transaction }); |
|||
labelIds = labelIds.concat(dbLabels.map(l => l.id)); |
|||
} |
|||
if (labelIds.length) { |
|||
await models.QrcodeLabelsQrcode.bulkCreate(labelIds.map(l => ({ |
|||
labelId: l, qrcodeId: dbQrcode.id, publicityInfoId: dbPublicityInfo.id |
|||
})), { transaction }); |
|||
} |
|||
} |
|||
|
|||
if (type !== '链接') { |
|||
if (!files || !files.length) throw new Error('缺少请求参数: files'); |
|||
await models.QrcodeFiles.bulkCreate(files.map(f => ({ |
|||
fileName: f.customFileName ? f.customFileName : f.name.split('.').slice(0, -1).join('.'), |
|||
fileSize: f.size, |
|||
fileUrl: f.storageUrl, |
|||
previewImgUrl: f.previewImgUrl, |
|||
qrcodeId: dbQrcode.id, |
|||
publicityInfoId: dbPublicityInfo.id, |
|||
})), { transaction }); |
|||
} |
|||
|
|||
ctx.status = 204; |
|||
ctx.body = { message: '新建宣传信息成功' }; |
|||
await transaction.commit(); |
|||
} catch (error) { |
|||
await transaction.rollback(); |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { message: '新建宣传信息失败' }; |
|||
} |
|||
} |
|||
|
|||
async function updatePublicityInfo(ctx, next) { |
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
const { id } = ctx.params; |
|||
const body = ctx.request.body; |
|||
await models.Department.update( |
|||
body, |
|||
{ where: { id: id } } |
|||
) |
|||
ctx.status = 204; |
|||
ctx.body = { message: '修改部门成功' } |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { message: '修改部门失败' } |
|||
} |
|||
} |
|||
|
|||
async function delPublicityInfo(ctx, next) { |
|||
const transaction = await ctx.fs.dc.orm.transaction(); |
|||
let errMsg = "删除宣传信息失败"; |
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
const { id } = ctx.params; |
|||
const { qrcodeId } = ctx.query; |
|||
|
|||
await models.QrcodeLabelsQrcode.destroy({ |
|||
where: { publicityInfoId: id }, |
|||
transaction |
|||
}); |
|||
await models.QrcodeFiles.destroy({ |
|||
where: { publicityInfoId: id }, |
|||
transaction |
|||
}); |
|||
await models.PublicityInfo.destroy({ |
|||
where: { id: id }, |
|||
transaction: transaction, |
|||
}); |
|||
await models.Qrcode.destroy({ |
|||
where: { id: qrcodeId }, |
|||
transaction |
|||
}); |
|||
|
|||
ctx.status = 204; |
|||
ctx.body = { message: '删除宣传信息成功' } |
|||
transaction.commit(); |
|||
} catch (error) { |
|||
transaction.rollback(); |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { message: error } |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
getPublicityInfo, |
|||
createPublicityInfo, |
|||
updatePublicityInfo, |
|||
delPublicityInfo, |
|||
} |
@ -0,0 +1,56 @@ |
|||
'use strict'; |
|||
|
|||
async function getQrcode(ctx, next) { |
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
const { limit, page, name, key } = ctx.query |
|||
|
|||
let options = { |
|||
where: {}, |
|||
order: [['id', 'DESC']], |
|||
include: [] |
|||
} |
|||
if (name) { |
|||
options.where.name = { $like: `%${name}%` } |
|||
} |
|||
if (key) { |
|||
options.where.key = key |
|||
options.include.push({ |
|||
model: models.QrcodeFiles, |
|||
}) |
|||
options.distinct = true |
|||
} |
|||
|
|||
const res = await models.Qrcode.findAndCountAll(options) |
|||
|
|||
ctx.status = 200; |
|||
ctx.body = res |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { |
|||
"message": "获取二维码失败" |
|||
} |
|||
} |
|||
} |
|||
|
|||
async function createQrcode(ctx, next) { |
|||
const models = ctx.fs.dc.models; |
|||
try { |
|||
let rslt = ctx.request.body; |
|||
|
|||
const qrcode = await models.Qrcode.create(rslt) |
|||
|
|||
ctx.status = 204; |
|||
ctx.body = { message: '新增二维码成果', id: qrcode.id } |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { message: '新增二维码失败' } |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
getQrcode, |
|||
createQrcode, |
|||
} |
@ -0,0 +1,74 @@ |
|||
/* eslint-disable*/ |
|||
'use strict'; |
|||
|
|||
module.exports = dc => { |
|||
const DataTypes = dc.ORM; |
|||
const sequelize = dc.orm; |
|||
const PublicityInfo = sequelize.define("publicityInfo", { |
|||
id: { |
|||
type: DataTypes.INTEGER, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: null, |
|||
primaryKey: true, |
|||
field: "id", |
|||
autoIncrement: true, |
|||
}, |
|||
name: { |
|||
type: DataTypes.STRING, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: "宣传标题", |
|||
primaryKey: false, |
|||
field: "name", |
|||
autoIncrement: false, |
|||
}, |
|||
type: { |
|||
type: DataTypes.STRING, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: "类型(图片|文件|链接|视频)", |
|||
primaryKey: false, |
|||
field: "type", |
|||
autoIncrement: false, |
|||
}, |
|||
time: { |
|||
type: DataTypes.DATE, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: "创建/更新时间", |
|||
primaryKey: false, |
|||
field: "time", |
|||
autoIncrement: false, |
|||
}, |
|||
qrcodeId: { |
|||
type: DataTypes.INTEGER, |
|||
allowNull: true, |
|||
defaultValue: null, |
|||
comment: "关联二维码ID", |
|||
primaryKey: false, |
|||
field: "qrcode_id", |
|||
autoIncrement: false, |
|||
references: { |
|||
model: "qrcode", |
|||
key: "id" |
|||
} |
|||
}, |
|||
link: { |
|||
type: DataTypes.STRING, |
|||
allowNull: true, |
|||
defaultValue: null, |
|||
comment: "type为链接时的链接地址", |
|||
primaryKey: false, |
|||
field: "link", |
|||
autoIncrement: false, |
|||
}, |
|||
}, { |
|||
tableName: "publicity_info", |
|||
comment: "", |
|||
indexes: [] |
|||
}); |
|||
|
|||
dc.models.PublicityInfo = PublicityInfo; |
|||
return PublicityInfo; |
|||
}; |
@ -0,0 +1,70 @@ |
|||
/* eslint-disable*/ |
|||
'use strict'; |
|||
|
|||
module.exports = dc => { |
|||
const DataTypes = dc.ORM; |
|||
const sequelize = dc.orm; |
|||
const Qrcode = sequelize.define("qrcode", { |
|||
id: { |
|||
type: DataTypes.INTEGER, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: null, |
|||
primaryKey: true, |
|||
field: "id", |
|||
autoIncrement: true, |
|||
}, |
|||
url: { |
|||
type: DataTypes.STRING, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: "二维码图片存储链接", |
|||
primaryKey: false, |
|||
field: "url", |
|||
autoIncrement: false, |
|||
}, |
|||
name: { |
|||
type: DataTypes.STRING, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: "二维码名称", |
|||
primaryKey: false, |
|||
field: "name", |
|||
autoIncrement: false, |
|||
}, |
|||
type: { |
|||
type: DataTypes.STRING, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: "类型(图片|文件|链接|视频)", |
|||
primaryKey: false, |
|||
field: "type", |
|||
autoIncrement: false, |
|||
}, |
|||
key: { |
|||
type: DataTypes.UUID, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: "二维码唯一标识", |
|||
primaryKey: false, |
|||
field: "key", |
|||
autoIncrement: false, |
|||
}, |
|||
logo: { |
|||
type: DataTypes.STRING, |
|||
allowNull: true, |
|||
defaultValue: null, |
|||
comment: "二维码中间的照片", |
|||
primaryKey: false, |
|||
field: "logo", |
|||
autoIncrement: false, |
|||
}, |
|||
}, { |
|||
tableName: "qrcode", |
|||
comment: "", |
|||
indexes: [] |
|||
}); |
|||
|
|||
dc.models.Qrcode = Qrcode; |
|||
return Qrcode; |
|||
}; |
@ -0,0 +1,87 @@ |
|||
/* eslint-disable*/ |
|||
'use strict'; |
|||
|
|||
module.exports = dc => { |
|||
const DataTypes = dc.ORM; |
|||
const sequelize = dc.orm; |
|||
const QrcodeFiles = sequelize.define("qrcodeFiles", { |
|||
id: { |
|||
type: DataTypes.INTEGER, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: null, |
|||
primaryKey: true, |
|||
field: "id", |
|||
autoIncrement: true, |
|||
}, |
|||
fileName: { |
|||
type: DataTypes.STRING, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: "文件名", |
|||
primaryKey: false, |
|||
field: "file_name", |
|||
autoIncrement: false, |
|||
}, |
|||
fileSize: { |
|||
type: DataTypes.INTEGER, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: "文件大小(byte)", |
|||
primaryKey: false, |
|||
field: "file_size", |
|||
autoIncrement: false, |
|||
}, |
|||
fileUrl: { |
|||
type: DataTypes.STRING, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: "文件存储链接", |
|||
primaryKey: false, |
|||
field: "file_url", |
|||
autoIncrement: false, |
|||
}, |
|||
previewImgUrl: { |
|||
type: DataTypes.STRING, |
|||
allowNull: true, |
|||
defaultValue: null, |
|||
comment: "文件预览图链接", |
|||
primaryKey: false, |
|||
field: "preview_img_url", |
|||
autoIncrement: false, |
|||
}, |
|||
qrcodeId: { |
|||
type: DataTypes.INTEGER, |
|||
allowNull: true, |
|||
defaultValue: null, |
|||
comment: "关联二维码ID", |
|||
primaryKey: false, |
|||
field: "qrcode_id", |
|||
autoIncrement: false, |
|||
references: { |
|||
model: "qrcode", |
|||
key: "id" |
|||
} |
|||
}, |
|||
publicityInfoId: { |
|||
type: DataTypes.INTEGER, |
|||
allowNull: true, |
|||
defaultValue: null, |
|||
comment: "关联宣传信息ID", |
|||
primaryKey: false, |
|||
field: "publicity_info_id", |
|||
autoIncrement: false, |
|||
references: { |
|||
model: "publicity_info", |
|||
key: "id" |
|||
} |
|||
}, |
|||
}, { |
|||
tableName: "qrcode_files", |
|||
comment: "", |
|||
indexes: [] |
|||
}); |
|||
|
|||
dc.models.QrcodeFiles = QrcodeFiles; |
|||
return QrcodeFiles; |
|||
}; |
@ -0,0 +1,34 @@ |
|||
/* eslint-disable*/ |
|||
'use strict'; |
|||
|
|||
module.exports = dc => { |
|||
const DataTypes = dc.ORM; |
|||
const sequelize = dc.orm; |
|||
const QrcodeLabels = sequelize.define("qrcodeLabels", { |
|||
id: { |
|||
type: DataTypes.INTEGER, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: null, |
|||
primaryKey: true, |
|||
field: "id", |
|||
autoIncrement: true, |
|||
}, |
|||
name: { |
|||
type: DataTypes.STRING, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: "标签名", |
|||
primaryKey: false, |
|||
field: "name", |
|||
autoIncrement: false, |
|||
}, |
|||
}, { |
|||
tableName: "qrcode_labels", |
|||
comment: "", |
|||
indexes: [] |
|||
}); |
|||
|
|||
dc.models.QrcodeLabels = QrcodeLabels; |
|||
return QrcodeLabels; |
|||
}; |
@ -0,0 +1,64 @@ |
|||
/* eslint-disable*/ |
|||
'use strict'; |
|||
|
|||
module.exports = dc => { |
|||
const DataTypes = dc.ORM; |
|||
const sequelize = dc.orm; |
|||
const QrcodeLabelsQrcode = sequelize.define("qrcode_labels_qrcode", { |
|||
id: { |
|||
type: DataTypes.INTEGER, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: null, |
|||
primaryKey: true, |
|||
field: "id", |
|||
autoIncrement: true, |
|||
}, |
|||
labelId: { |
|||
type: DataTypes.INTEGER, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: null, |
|||
primaryKey: false, |
|||
field: "label_id", |
|||
autoIncrement: false, |
|||
references: { |
|||
model: "qrcode_labels", |
|||
key: "id" |
|||
} |
|||
}, |
|||
qrcodeId: { |
|||
type: DataTypes.INTEGER, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: null, |
|||
primaryKey: false, |
|||
field: "qrcode_id", |
|||
autoIncrement: false, |
|||
references: { |
|||
model: "qrcode", |
|||
key: "id" |
|||
} |
|||
}, |
|||
publicityInfoId: { |
|||
type: DataTypes.INTEGER, |
|||
allowNull: true, |
|||
defaultValue: null, |
|||
comment: null, |
|||
primaryKey: false, |
|||
field: "publicity_info_id", |
|||
autoIncrement: false, |
|||
references: { |
|||
model: "publicity_info", |
|||
key: "id" |
|||
} |
|||
}, |
|||
}, { |
|||
tableName: "qrcode_labels_qrcode", |
|||
comment: "", |
|||
indexes: [] |
|||
}); |
|||
|
|||
dc.models.QrcodeLabelsQrcode = QrcodeLabelsQrcode; |
|||
return QrcodeLabelsQrcode; |
|||
}; |
@ -0,0 +1,15 @@ |
|||
'use strict'; |
|||
|
|||
const label = require('../../controllers/label/index'); |
|||
|
|||
module.exports = function (app, router, opts) { |
|||
|
|||
app.fs.api.logAttr['GET/label'] = { content: '获取标签', visible: false }; |
|||
router.get('/label', label.getLabels); |
|||
|
|||
app.fs.api.logAttr['POST/label/add'] = { content: '新增标签', visible: true }; |
|||
router.post('/label/add', label.createLabels); |
|||
|
|||
app.fs.api.logAttr['DELETE/label/:id'] = { content: '删除标签', visible: true }; |
|||
router.del('/label/:id', label.delLabels); |
|||
}; |
@ -0,0 +1,18 @@ |
|||
'use strict'; |
|||
|
|||
const publicityInfo = require('../../controllers/publicityInfo/index'); |
|||
|
|||
module.exports = function (app, router, opts) { |
|||
|
|||
app.fs.api.logAttr['GET/publicityInfo'] = { content: '获取宣传信息', visible: false }; |
|||
router.get('/publicityInfo', publicityInfo.getPublicityInfo); |
|||
|
|||
app.fs.api.logAttr['POST/publicityInfo/add'] = { content: '新增宣传信息', visible: true }; |
|||
router.post('/publicityInfo/add', publicityInfo.createPublicityInfo); |
|||
|
|||
app.fs.api.logAttr['PUT/publicityInfo/:id/modify'] = { content: '修改宣传信息', visible: true }; |
|||
router.put('/publicityInfo/:id/modify', publicityInfo.updatePublicityInfo); |
|||
|
|||
app.fs.api.logAttr['DELETE/publicityInfo/:id/del'] = { content: '删除宣传信息', visible: true }; |
|||
router.del('/publicityInfo/:id/del', publicityInfo.delPublicityInfo); |
|||
}; |
@ -0,0 +1,13 @@ |
|||
'use strict'; |
|||
|
|||
const qrcode = require('../../controllers/qrcode/index'); |
|||
|
|||
module.exports = function (app, router, opts) { |
|||
|
|||
app.fs.api.logAttr['GET/qrcode'] = { content: '获取二维码', visible: false }; |
|||
router.get('/qrcode', qrcode.getQrcode); |
|||
|
|||
app.fs.api.logAttr['POST/qrcode/add'] = { content: '新增二维码', visible: true }; |
|||
router.post('/qrcode/add', qrcode.createQrcode); |
|||
|
|||
}; |
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 1.0 MiB |
After Width: | Height: | Size: 3.8 KiB |
@ -0,0 +1,52 @@ |
|||
import React, { useRef } from 'react' |
|||
import { Spin, Upload, message, Modal, Card, Button, Input, Form } from 'antd' |
|||
import Uploads from '../Uploads' |
|||
export default function FileInfoForm({ }) { |
|||
const [form] = Form.useForm(); |
|||
|
|||
const handleOk = () => { |
|||
form.validateFields() |
|||
.then((values) => { |
|||
console.log(values, 'valuesvalues') |
|||
}) |
|||
.catch((info) => { |
|||
console.log('Validate Failed:', info); |
|||
}); |
|||
} |
|||
|
|||
return ( |
|||
<Form |
|||
form={form} |
|||
layout="inline" |
|||
name="preview_form" |
|||
initialValues={{}} |
|||
> |
|||
<Form.Item |
|||
name="fileName" |
|||
label="文件名" |
|||
rules={[{ required: true, message: '请输入文件名' }]} |
|||
> |
|||
<Input style={{ width: '200px' }} /> |
|||
</Form.Item> |
|||
<Form.Item |
|||
name="previewImg" |
|||
label="预览图上传" |
|||
rules={[]} |
|||
> |
|||
<Uploads |
|||
listType='picture-card' |
|||
uploadType='image' |
|||
maxFilesNum={1} |
|||
maxFileSize={5} |
|||
isQiniu={true} |
|||
fileTypes={["png", "jpg"]} |
|||
defaultValue={([]).map(s => { |
|||
return { |
|||
storageUrl: s |
|||
} |
|||
})} |
|||
/> |
|||
</Form.Item> |
|||
</Form> |
|||
) |
|||
} |
@ -0,0 +1,413 @@ |
|||
'use strict'; |
|||
|
|||
import React, { Component, useRef } from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
import { Spin, Upload, message, Modal, Card, Button, Input, Form } from 'antd'; |
|||
import Uploads from '../Uploads'; |
|||
import moment from 'moment'; |
|||
import { PlusOutlined, UploadOutlined, CloseOutlined } from '@ant-design/icons'; |
|||
import './index.less' |
|||
|
|||
class QrcodeFilesUpload extends Component { |
|||
constructor(props) { |
|||
super(props); |
|||
this.ApiRoot = localStorage.getItem('tyApiRoot') |
|||
this.qnDomain = localStorage.getItem('qnDomain'); |
|||
this.aliAdmin = localStorage.getItem('aliAdmin'); |
|||
this.state = { |
|||
fileUploading: false, |
|||
fileList: [], |
|||
curPreviewPic: '', |
|||
curPreviewVideo: '', |
|||
delPicIng: false, |
|||
removeFilesList: [] |
|||
}; |
|||
} |
|||
|
|||
dealName = (uploaded) => { |
|||
let realName = uploaded?.split('/')[2] |
|||
return realName |
|||
} |
|||
|
|||
setFileList = (nextEditData, isQiniu, isAli) => { |
|||
let defaultFileList = []; |
|||
defaultFileList = nextEditData.map((u, index) => { |
|||
let fileUrl = |
|||
isQiniu ? `/_file-server/${u.storageUrl}` |
|||
: isAli ? `/_file-ali-server/${u.storageUrl}` |
|||
: `${this.ApiRoot}/${u.storageUrl}`; |
|||
|
|||
return { |
|||
uid: -index - 1, |
|||
name: this.dealName(u.storageUrl), |
|||
status: 'done', |
|||
storageUrl: u.storageUrl, |
|||
url: fileUrl, |
|||
size: u.size || -1, |
|||
customFileName: u.customFileName, |
|||
previewImgUrl: u.previewImgUrl, |
|||
}; |
|||
}); |
|||
this.setState({ |
|||
fileList: defaultFileList |
|||
}); |
|||
}; |
|||
|
|||
componentDidMount() { |
|||
const { value, defaultValue, isQiniu, isAli } = this.props; |
|||
if (defaultValue) { |
|||
this.setFileList(defaultValue, isQiniu, isAli) |
|||
} |
|||
} |
|||
|
|||
UNSAFE_componentWillReceiveProps(np) { |
|||
const { dispatch, value: thisEditData, onChange } = this.props; |
|||
const { value: nextEditData, isQiniu, isAli } = np; |
|||
if (nextEditData && nextEditData.length) { |
|||
if (!thisEditData || !this.state.fileList.length) { |
|||
this.setFileList(nextEditData, isQiniu, isAli); |
|||
} else if (nextEditData.length != thisEditData.length) { |
|||
this.setFileList(nextEditData, isQiniu, isAli); |
|||
} else { |
|||
let repeat = true; |
|||
for (let i = 0; i < thisEditData.length; i++) { |
|||
if (thisEditData[i] != nextEditData[i]) { |
|||
repeat = false; |
|||
break; |
|||
} |
|||
} |
|||
if (!repeat) { |
|||
this.setFileList(nextEditData, isQiniu, isAli); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
render() { |
|||
const UploadPath = { |
|||
project: ['txt', 'dwg', 'doc', 'docx', 'xls', 'xlsx', 'csv', 'pdf', 'pptx', 'png', 'jpg', 'svg', 'jpeg', 'rar', 'zip', 'jpeg', 'mp4'], |
|||
report: ['doc', 'docx', 'xls', 'xlsx', 'csv', 'pdf'], |
|||
data: ['txt', 'xls', 'xlsx', 'csv'], |
|||
image: ['png', 'jpg', 'svg', 'jpeg'], |
|||
three: ['js'], |
|||
video: ['mp4'] |
|||
}; |
|||
/** |
|||
* uploadType 【string】 主要区别文件上传路径 以及类型 以 web/routes/attachment/index.js 中 UploadPath 的 key 值为准;默认 project; |
|||
* disabled 【boolean】 上传是否可用 |
|||
* maxFilesNum 【number】 最大上传数量 |
|||
* fileTypes 【array[string]】 可允许上传的文件类型; |
|||
* maxFileSize 【number】 单个文件最大大小 M |
|||
* listType 【antd】 upload 组件的属性 |
|||
* onChange 【function】 文件数量变化时候回调 返回文件 |
|||
* value 【array[obj]】 编辑数据 [{url:'xxx', [size:999]}] |
|||
* onStateChange 【function】 文件状态改变回调函数 上传中 return { uploading:true/false } |
|||
*/ |
|||
const { |
|||
uploadType, |
|||
disabled, |
|||
maxFilesNum, |
|||
fileTypes, |
|||
maxFileSize, |
|||
listType, |
|||
onChange = () => { }, |
|||
value, |
|||
showUploadList, |
|||
onStateChange, |
|||
isQiniu, |
|||
isAli, |
|||
} = this.props; |
|||
const { fileList, curPreviewPic, curPreviewVideo, delPicIng, removeFilesList } = this.state; |
|||
const that = this; |
|||
let uploadType_ = uploadType || 'project'; |
|||
let maxFilesNum_ = maxFilesNum || 1; |
|||
let defaultFileTypes = fileTypes || UploadPath[uploadType_]; |
|||
// debugger
|
|||
const uploadProps = { |
|||
name: 'checkFile_', |
|||
multiple: false, |
|||
showUploadList: showUploadList || true, |
|||
action: |
|||
isQiniu ? `/_upload/attachments/${uploadType_}` |
|||
: isAli ? `/_upload/attachments/ali/${uploadType_}` |
|||
: `${this.ApiRoot}/attachments/${uploadType_}`, |
|||
listType: listType || 'text', |
|||
disabled: disabled, |
|||
beforeUpload: (file) => { |
|||
if (fileList.length >= maxFilesNum_) { |
|||
message.warning(`最多选择${maxFilesNum_}个文件上传`); |
|||
return false; |
|||
} |
|||
if (file.name.length > 60) { |
|||
message.warning(`文件名过长(大于60字符),请修改后上传`); |
|||
return false; |
|||
} |
|||
const extNames = file.name?.split('.'); |
|||
// var reg = /^[\.\s\u4e00-\u9fa5a-zA-Z0-9_-]{0,}$/;
|
|||
// if (!reg.exec(file.name)) {
|
|||
// message.warning(`文件名包含除字母、汉字、数字、中划线、下划线之外的字符,请修改后上传`);
|
|||
// return false;
|
|||
// }
|
|||
let isDAE = false; |
|||
if (extNames.length > 0) { |
|||
let fileType = extNames[extNames.length - 1].toLowerCase(); |
|||
isDAE = defaultFileTypes.some((f) => f == fileType); |
|||
} |
|||
if (!isDAE) { |
|||
message.error(`只能上传 ${defaultFileTypes.join()} 格式的文件!`); |
|||
return false; |
|||
} |
|||
const isLt = file.size / 1024 / 1024 < (maxFileSize || 3); |
|||
if (!isLt) { |
|||
message.error(`文件必须小于${maxFileSize || 3}MB!`); |
|||
return false; |
|||
} |
|||
this.setState({ |
|||
fileUploading: true |
|||
}); |
|||
if (onStateChange) { |
|||
onStateChange({ uploading: true }); |
|||
} |
|||
}, |
|||
onChange(info) { |
|||
const status = info.file.status; |
|||
if (status === 'uploading') { |
|||
that.setState({ |
|||
fileList: info.fileList |
|||
}); |
|||
} |
|||
if (status === 'done') { |
|||
let { uploaded, url } = info.file.response; |
|||
let size = info.file.size; |
|||
let nextFileList = fileList; |
|||
nextFileList[nextFileList.length - 1] = { |
|||
uid: -moment().unix(), |
|||
name: that.dealName(uploaded), |
|||
status: 'done', |
|||
storageUrl: uploaded, |
|||
url: |
|||
isQiniu ? '/_file-server/' + uploaded : |
|||
isAli ? `/_file-ali-server/${uploaded}` : |
|||
url, |
|||
size: size |
|||
}; |
|||
onChange(nextFileList); |
|||
that.setState({ |
|||
fileUploading: false, |
|||
fileList: nextFileList |
|||
}); |
|||
if (onStateChange) { |
|||
onStateChange({ uploading: false }); |
|||
} |
|||
} else if (status === 'error') { |
|||
that.setState({ |
|||
fileUploading: false |
|||
}); |
|||
message.error(`${info.file.name} 上传失败,请重试`); |
|||
if (onStateChange) { |
|||
onStateChange({ uploading: false }); |
|||
} |
|||
} |
|||
}, |
|||
onRemove(file) { |
|||
let nextFileList = []; |
|||
fileList.map((f, i) => { |
|||
if (f.uid != file.uid) { |
|||
nextFileList.push(f); |
|||
} |
|||
}); |
|||
let nextRemoveFiles = removeFilesList.concat([file.storageUrl]); |
|||
if (curPreviewPic == file.url) { |
|||
that.setState({ |
|||
curPreviewPic: '' |
|||
}); |
|||
} |
|||
if (curPreviewVideo == file.url) { |
|||
that.setState({ |
|||
curPreviewVideo: '' |
|||
}); |
|||
} |
|||
onChange(nextFileList); |
|||
that.setState({ |
|||
fileList: nextFileList, |
|||
removeFilesList: nextRemoveFiles |
|||
}); |
|||
}, |
|||
onPreview(file) { |
|||
let filePostfix = file.url?.split('.').pop(); |
|||
filePostfix = filePostfix.toLowerCase(); |
|||
if (UploadPath.image.some((img) => img == filePostfix)) { |
|||
that.setState({ |
|||
curPreviewPic: file.url |
|||
}); |
|||
} else if (UploadPath.video.some((img) => img == filePostfix)) { |
|||
that.setState({ |
|||
curPreviewVideo: file.url |
|||
}); |
|||
} else { |
|||
//message.warn('仅支持图片预览');
|
|||
preview(file.storageUrl) |
|||
} |
|||
} |
|||
}; |
|||
|
|||
const preview = (url) => { |
|||
let link = isQiniu ? encodeURI(`${this.qnDomain}/${url}`) : |
|||
isAli ? encodeURI(`${this.aliAdmin}/${url}`) : '' |
|||
if (link) |
|||
if (url.indexOf("pdf") !== -1 || url.indexOf("csv") !== -1) { |
|||
window.open(link) |
|||
} else { |
|||
window.open(`https://view.officeapps.live.com/op/view.aspx?src=${link}`) |
|||
} |
|||
} |
|||
|
|||
let fileList_ = fileList |
|||
//下载文件
|
|||
const handleDownload = (file) => { |
|||
saveAs(file) |
|||
}; |
|||
const saveAs = (file) => { |
|||
const link = document.createElement('a'); |
|||
link.href = file.url; |
|||
link.download = file.name; |
|||
link.style.display = 'none'; |
|||
link.click(); |
|||
} |
|||
|
|||
//自定义下载
|
|||
|
|||
return ( |
|||
<div> |
|||
<Spin spinning={delPicIng}> |
|||
<Upload |
|||
{...uploadProps} |
|||
fileList={fileList_} |
|||
showUploadList={{ showDownloadIcon: true }} |
|||
onDownload={handleDownload} |
|||
itemRender={(originNode, file, currFileList) => { |
|||
const fileName = file.name?.split('.').slice(0, -1).join('.') |
|||
const extension = file.name?.split('.').pop() |
|||
return <> |
|||
{originNode} |
|||
<Form |
|||
layout="inline" |
|||
name="preview_form" |
|||
initialValues={{ |
|||
fileName: fileName |
|||
}} |
|||
> |
|||
<Form.Item |
|||
name="fileName" |
|||
label="文件标题" |
|||
rules={[]} |
|||
> |
|||
<Input |
|||
onChange={({ target: { value } }) => { |
|||
const fileInfo = { |
|||
...file, |
|||
customFileName: value, |
|||
}; |
|||
const nextFileList = currFileList.toSpliced( |
|||
currFileList.findIndex(item => item.storageUrl === file.storageUrl), |
|||
1, |
|||
fileInfo, |
|||
); |
|||
onChange(nextFileList); |
|||
this.setState({ fileList: nextFileList }) |
|||
}} |
|||
placeholder="不填写默认为上传的文件名" |
|||
style={{ width: '220px' }} |
|||
/> |
|||
</Form.Item> |
|||
{!UploadPath.image.includes(extension) && <Form.Item |
|||
name="previewImg" |
|||
label="预览图上传" |
|||
rules={[]} |
|||
> |
|||
<Uploads |
|||
listType='picture-card' |
|||
uploadType='image' |
|||
maxFilesNum={1} |
|||
maxFileSize={5} |
|||
isQiniu={true} |
|||
fileTypes={["png", "jpg", "jpeg"]} |
|||
onChange={(previewFile) => { |
|||
const fileInfo = { |
|||
...file, |
|||
previewImgUrl: previewFile[0]?.storageUrl, |
|||
}; |
|||
const nextFileList = currFileList.toSpliced( |
|||
currFileList.findIndex(item => item.storageUrl === file.storageUrl), |
|||
1, |
|||
fileInfo, |
|||
); |
|||
onChange(nextFileList); |
|||
this.setState({ fileList: nextFileList }) |
|||
}} |
|||
defaultValue={([]).map(s => { |
|||
return { |
|||
storageUrl: s |
|||
} |
|||
})} |
|||
/> |
|||
</Form.Item>} |
|||
</Form> |
|||
</> |
|||
}} |
|||
> |
|||
{ |
|||
disabled ? ('') : |
|||
listType == 'picture-card' ? |
|||
( |
|||
fileList.length >= maxFilesNum_ ? null : ( |
|||
<div style={{}}> |
|||
<PlusOutlined /> |
|||
<div>添加附件</div> |
|||
</div> |
|||
) |
|||
) : ( |
|||
<Button disabled={fileList.length >= maxFilesNum_} icon={<UploadOutlined />}> 文件上传 </Button> |
|||
) |
|||
} |
|||
</Upload> |
|||
{ |
|||
curPreviewPic ? ( |
|||
<Card bodyStyle={{ padding: 8 }}> |
|||
<div style={{ marginBottom: 8 }} > |
|||
<span>图片预览</span> |
|||
<span style={{ float: 'right' }} onClick={() => { this.setState({ curPreviewPic: '' }) }}> |
|||
<CloseOutlined style={{ fontSize: 20 }} /> |
|||
</span> |
|||
</div> |
|||
<img style={{ width: '100%' }} src={curPreviewPic} /> |
|||
</Card> |
|||
) : '' |
|||
} |
|||
{ |
|||
curPreviewVideo ? (<Card bodyStyle={{ padding: 8 }}> |
|||
<div style={{ marginBottom: 8 }} > |
|||
<span>视频预览</span> |
|||
<span style={{ float: 'right' }} onClick={() => { this.setState({ curPreviewVideo: '' }) }}> |
|||
<CloseOutlined style={{ fontSize: 20 }} /> |
|||
</span> |
|||
</div> |
|||
<video controls style={{ width: '100%' }}> |
|||
<source src={curPreviewVideo} type="video/mp4"></source> |
|||
</video> |
|||
</Card>) : '' |
|||
} |
|||
</Spin> |
|||
</div> |
|||
); |
|||
} |
|||
} |
|||
|
|||
function mapStateToProps(state) { |
|||
const { auth } = state |
|||
return { |
|||
user: auth.user |
|||
}; |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(QrcodeFilesUpload); |
@ -0,0 +1,38 @@ |
|||
'use strict'; |
|||
|
|||
import { basicAction } from '@peace/utils' |
|||
import { ApiTable } from '$utils' |
|||
|
|||
export function getLabels(query) { |
|||
return (dispatch) => basicAction({ |
|||
type: 'get', |
|||
query, |
|||
dispatch, |
|||
actionType: 'GET_LABELS', |
|||
url: ApiTable.getLabels, |
|||
msg: { error: '获取标签失败', }, |
|||
reducer: { name: 'labels' } |
|||
}); |
|||
} |
|||
|
|||
export function createLabels(data) { |
|||
return (dispatch) => basicAction({ |
|||
type: 'post', |
|||
data, |
|||
dispatch, |
|||
actionType: 'POST_LABELS', |
|||
url: ApiTable.createLabels, |
|||
msg: { option: '新增标签', }, |
|||
}); |
|||
} |
|||
|
|||
export function delLabels(id) { |
|||
return (dispatch) => basicAction({ |
|||
type: 'del', |
|||
dispatch, |
|||
actionType: 'DEL_LABELS', |
|||
url: ApiTable.delLabels.replace('{id}', id), |
|||
msg: { option: '删除标签', }, |
|||
}); |
|||
} |
|||
|
@ -0,0 +1,273 @@ |
|||
import React, { useState, useEffect } from 'react'; |
|||
import { Button, Form, Input, Modal, Select, Divider, Row, Col, Image, message } from 'antd'; |
|||
import QrcodeFilesUpload from '../../../components/QrcodeFilesUpload'; |
|||
import { Uploads } from '../../../components'; |
|||
import { connect } from 'react-redux'; |
|||
import { getLabels } from '../actions/label'; |
|||
import { createPublicityInfo, updatePublicityInfo } from '../actions/publicityInfoConfig'; |
|||
import QrCodeWithLogo from 'qr-code-with-logo' |
|||
import { v4 as uuidv4 } from 'uuid'; |
|||
import request from 'superagent'; |
|||
|
|||
const PublicityInfoModal = ({ visible, onCancel, updateList, dispatch, curRecord, labels }) => { |
|||
const [form] = Form.useForm(); |
|||
|
|||
const [type, setType] = useState('图片'); |
|||
const [qrcode, setQrcode] = useState(''); |
|||
|
|||
useEffect(() => { |
|||
getLabelsData() |
|||
if (curRecord) { |
|||
console.log(curRecord, 'curRecord') |
|||
setType(curRecord.type) |
|||
} else { |
|||
draw() |
|||
} |
|||
}, []) |
|||
|
|||
const getLabelsData = () => { |
|||
dispatch(getLabels({})); |
|||
} |
|||
|
|||
const draw = () => { |
|||
const ctx = document.getElementById("qrcodeCanvas").getContext("2d"); |
|||
ctx.fillStyle = "#f5f5f5"; |
|||
ctx.fillRect(0, 0, 220, 220); |
|||
ctx.fillStyle = "black"; |
|||
ctx.font = "14px serif"; |
|||
ctx.fillText("此处预览二维码", 60, 110); |
|||
} |
|||
|
|||
const handleSubmit = async (values) => { |
|||
console.log(values, 'values') |
|||
if (curRecord) { |
|||
// dispatch(updatePublicityInfo(params)).then(res => {
|
|||
// if (res.success) {
|
|||
// // form.resetFields();
|
|||
// // onCancel();
|
|||
// }
|
|||
// })
|
|||
} else { |
|||
const key = uuidv4(); |
|||
// 生成二维码
|
|||
let qrcodeUrl = ''; |
|||
const qrcodeCanvas = document.getElementById('qrcodeCanvas') |
|||
let options = { |
|||
canvas: qrcodeCanvas, |
|||
width: 220, |
|||
content: values.type === '链接' |
|||
? values.link |
|||
: `${window.location.protocol}//${window.location.host}/scan?key=${key}`, |
|||
} |
|||
if (values.qrcodeImg?.length) { |
|||
options.logo = { |
|||
src: '/_file-server/' + values.qrcodeImg[0].storageUrl, |
|||
radius: 8 |
|||
} |
|||
} |
|||
await QrCodeWithLogo.toCanvas(options) |
|||
|
|||
// 上传二维码
|
|||
try { |
|||
const blob = await canvasToBlob(qrcodeCanvas); |
|||
const formData = new FormData(); |
|||
formData.append('image', blob, values.name + '.png'); |
|||
const res = await request.post('/_upload/attachments/image', formData) |
|||
qrcodeUrl = res.body.uploaded |
|||
} catch (error) { |
|||
console.log(error); |
|||
message.error('二维码上传失败'); |
|||
return; |
|||
} |
|||
console.log(qrcodeUrl, 'qrcodeUrl') |
|||
dispatch(createPublicityInfo({ |
|||
...values, |
|||
qrcode: { url: qrcodeUrl, key }, |
|||
})).then(res => { |
|||
if (res.success) { |
|||
form.resetFields(); |
|||
onCancel(); |
|||
updateList(); |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
|
|||
const handleChange = (value) => { |
|||
console.log(`selected ${value}`); |
|||
}; |
|||
|
|||
return ( |
|||
<Modal |
|||
width={700} |
|||
visible={visible} |
|||
title={`${curRecord ? "编辑" : "新增"}宣传项`} |
|||
okText="确定" |
|||
cancelText="取消" |
|||
footer={[ |
|||
<Button key="submit" type="primary" onClick={() => { |
|||
form.validateFields() |
|||
.then((values) => { |
|||
handleSubmit(values) |
|||
}) |
|||
.catch((info) => { |
|||
console.log('Validate Failed:', info); |
|||
}); |
|||
}}> |
|||
生成二维码并保存 |
|||
</Button>, |
|||
// <Button type="primary" onClick={() => { }}>
|
|||
// 生成二维码
|
|||
// </Button>,
|
|||
<Button key="back" onClick={() => { |
|||
form.resetFields(); |
|||
onCancel(); |
|||
}}>取消</Button>, |
|||
]} |
|||
onCancel={() => { |
|||
form.resetFields(); |
|||
onCancel(); |
|||
}} |
|||
> |
|||
<Form |
|||
form={form} |
|||
layout="vertical" |
|||
name="form_in_modal" |
|||
initialValues={{ |
|||
...curRecord, |
|||
type: curRecord?.type || type, |
|||
}} |
|||
onValuesChange={() => { |
|||
}} |
|||
> |
|||
<Form.Item |
|||
name="name" |
|||
label="宣传标题" |
|||
rules={[{ required: true, message: '请输入宣传标题' }]} |
|||
> |
|||
<Input /> |
|||
</Form.Item> |
|||
<Form.Item |
|||
name="type" |
|||
label="类型" |
|||
rules={[{ required: true, message: '请选择类型' }]} |
|||
> |
|||
<Select |
|||
value={type} |
|||
options={[ |
|||
{ |
|||
value: '图片', |
|||
label: '图片', |
|||
}, |
|||
{ |
|||
value: '文件', |
|||
label: '文件', |
|||
}, |
|||
{ |
|||
value: '链接', |
|||
label: '链接', |
|||
}, |
|||
{ |
|||
value: '视频', |
|||
label: '视频', |
|||
}, |
|||
]} |
|||
style={{ width: '50%' }} |
|||
onChange={(value) => { setType(value); }} |
|||
/> |
|||
</Form.Item> |
|||
{type === '链接' ? <Form.Item |
|||
name="link" |
|||
label="链接地址" |
|||
rules={[{ required: true, message: '请填写链接地址' }]} |
|||
> |
|||
<Input placeholder='http://' /> |
|||
</Form.Item> : <Form.Item |
|||
name="files" |
|||
label={`${type}上传`} |
|||
rules={[{ required: true, message: '请上传文件' }]} |
|||
> |
|||
<QrcodeFilesUpload |
|||
listType='list' |
|||
maxFilesNum={50} |
|||
maxFileSize={500} |
|||
isQiniu={true} |
|||
uploadType={type === '图片' ? 'image' : type === '视频' ? 'video' : 'project'} |
|||
// fileTypes={["png", "jpg", "mp4"]}
|
|||
defaultValue={([]).map(s => { |
|||
return { |
|||
storageUrl: s |
|||
} |
|||
})} |
|||
/> |
|||
</Form.Item>} |
|||
<Divider /> |
|||
<Row> |
|||
<Col span={14}> |
|||
<Form.Item |
|||
name="qrcodeImg" |
|||
label="二维码照片" |
|||
rules={[]} |
|||
> |
|||
<Uploads |
|||
listType='picture-card' |
|||
uploadType='image' |
|||
maxFilesNum={1} |
|||
maxFileSize={5} |
|||
isQiniu={true} |
|||
fileTypes={["png", "jpg", "jpeg"]} |
|||
defaultValue={([]).map(s => { |
|||
return { |
|||
storageUrl: s |
|||
} |
|||
})} |
|||
/> |
|||
</Form.Item> |
|||
<Form.Item |
|||
name="labels" |
|||
label="标签" |
|||
rules={[]} |
|||
> |
|||
<Select |
|||
mode="tags" |
|||
style={{ width: '80%' }} |
|||
placeholder="输入或选择" |
|||
onChange={handleChange} |
|||
options={labels} |
|||
fieldNames={{ label: 'name', value: 'id' }} |
|||
/> |
|||
</Form.Item> |
|||
</Col> |
|||
<Col span={10}> |
|||
{qrcode |
|||
? <Image width={220} src={qrcode} /> |
|||
: <canvas id="qrcodeCanvas" width="220" height="220" /> |
|||
} |
|||
</Col> |
|||
</Row> |
|||
</Form> |
|||
</Modal> |
|||
); |
|||
}; |
|||
|
|||
function mapStateToProps(state) { |
|||
const { auth, labels } = state |
|||
|
|||
return { |
|||
user: auth.user, |
|||
labels: labels.data || [], |
|||
} |
|||
} |
|||
export default connect(mapStateToProps)(PublicityInfoModal); |
|||
|
|||
function canvasToBlob(canvas) { |
|||
return new Promise((resolve, reject) => { |
|||
canvas.toBlob((blob) => { |
|||
if (blob) { |
|||
resolve(blob); |
|||
} else { |
|||
reject(new Error('无法生成Blob对象')); |
|||
} |
|||
}); |
|||
}); |
|||
} |
@ -0,0 +1,141 @@ |
|||
import React, { useEffect, useState } from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
import { SafeArea, AutoCenter, Button, Card, Toast, Image, Popup, Tag, Space, WaterMark, Divider } from 'antd-mobile'; |
|||
import { getQrCode } from '../actions/qrCode'; |
|||
import './scanCode.less'; |
|||
|
|||
const fileType = { |
|||
report: ['doc', 'docx', 'xls', 'xlsx', 'csv', 'pdf', 'pptx'], |
|||
image: ['png', 'jpg', 'svg', 'jpeg'], |
|||
video: ['mp4'] |
|||
}; |
|||
|
|||
const ScanCode = ({ dispatch }) => { |
|||
const urlParams = new URLSearchParams(window.location.search); |
|||
const key = urlParams.get('key'); |
|||
const qnDomain = localStorage.getItem('qnDomain'); |
|||
|
|||
const [qrCode, setQrCode] = useState({}); |
|||
const [visible, setVisible] = useState(false) |
|||
const [link, setLink] = useState('') |
|||
|
|||
useEffect(() => { |
|||
if (!key) { |
|||
Toast.show({ |
|||
icon: 'fail', |
|||
content: '二维码参数错误', |
|||
}) |
|||
return |
|||
} |
|||
getQrCodeData(key) |
|||
}, []) |
|||
|
|||
const getQrCodeData = (key) => { |
|||
dispatch(getQrCode({ key })).then(res => { |
|||
console.log(res, 'res') |
|||
if (!res.payload?.data?.rows?.length) { |
|||
Toast.show({ icon: 'fail', content: '该二维码已被删除,无法访问' }) |
|||
return |
|||
} |
|||
setQrCode(res.payload.data.rows[0]) |
|||
}) |
|||
} |
|||
|
|||
const { qrcodeFiles } = qrCode; |
|||
|
|||
return (<> |
|||
<SafeArea position='top' /> |
|||
<AutoCenter>{qrCode.name}</AutoCenter> |
|||
<Divider /> |
|||
{ |
|||
qrcodeFiles?.map((f, i) => { |
|||
const extensionName = getFileExtension(f.fileUrl) |
|||
if (fileType.report.includes(extensionName)) { |
|||
return <Card key={i} |
|||
title={<Space> |
|||
<div className='text---' style={{ maxWidth: '60vw' }}>{f.fileName}</div> |
|||
<Tag round color='#2db7f5'>{extensionName}</Tag> |
|||
</Space>} |
|||
extra={<Button |
|||
size='small' |
|||
color='primary' |
|||
onClick={() => { window.open(`/_file-server/${f.fileUrl}`, '_self') }} |
|||
>下载</Button>} |
|||
> |
|||
<Image |
|||
src={f.previewImgUrl ? `/_file-server/${f.previewImgUrl}` : '/assets/images/document.svg'} |
|||
style={{ width: '100%', height: '200px' }} |
|||
fit='contain' |
|||
onClick={() => { |
|||
// const link = encodeURI(`https://view.officeapps.live.com/op/view.aspx?src=${qnDomain}/${f.fileUrl}`);
|
|||
const nextLink = encodeURI(`https://view.xdocin.com/view?src=${qnDomain}/${f.fileUrl}`); |
|||
setLink(nextLink) |
|||
setVisible(true) |
|||
}} |
|||
/> |
|||
</Card> |
|||
} |
|||
else if (fileType.image.includes(extensionName)) { |
|||
return <Card key={i} title={<Space> |
|||
<div className='text---' style={{ maxWidth: '70vw' }}>{f.fileName}</div> |
|||
<Tag round color='#2db7f5'>{extensionName}</Tag> |
|||
</Space>}> |
|||
<Image |
|||
src={`/_file-server/${f.fileUrl}`} |
|||
style={{ width: '100%', height: '200px' }} |
|||
fit='contain' |
|||
/> |
|||
</Card > |
|||
} |
|||
else if (fileType.video.includes(extensionName)) { |
|||
return <Card key={i} title={<Space> |
|||
<div className='text---' style={{ maxWidth: '70vw' }}>{f.fileName}</div> |
|||
<Tag round color='#2db7f5'>{extensionName}</Tag> |
|||
</Space>}> |
|||
<video |
|||
style={{ width: '100%' }} |
|||
src={`/_file-server/${f.fileUrl}`} |
|||
poster={f.previewImgUrl ? `/_file-server/${f.previewImgUrl}` : undefined} |
|||
controls |
|||
/> |
|||
</Card> |
|||
} |
|||
}) |
|||
} |
|||
<Popup |
|||
visible={visible} |
|||
onMaskClick={() => { |
|||
setVisible(false) |
|||
}} |
|||
position='bottom' |
|||
bodyStyle={{ height: '80vh' }} |
|||
closeOnMaskClick |
|||
> |
|||
<iframe |
|||
width="100%" |
|||
height="100%" |
|||
frameBorder='1' |
|||
src={link}> |
|||
</iframe> |
|||
<WaterMark content='FREESUM 飞尚科技' /> |
|||
</Popup> |
|||
<SafeArea position='bottom' /> |
|||
</>) |
|||
} |
|||
|
|||
function mapStateToProps(state) { |
|||
const { global } = state; |
|||
return { |
|||
actions: global.actions, |
|||
}; |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(ScanCode); |
|||
|
|||
const getFileExtension = (fileUrl) => { |
|||
const fileNameWithExtension = fileUrl.split('/').pop(); // 获取URL的最后一部分(即文件名和扩展名)
|
|||
const extension = fileNameWithExtension.split('.').pop(); // 从文件名中分离出扩展名
|
|||
|
|||
// 如果没有扩展名或文件名不含点,则返回空字符串
|
|||
return extension ? extension.toLowerCase() : ''; |
|||
}; |
@ -0,0 +1,16 @@ |
|||
// @import '~antd-mobile/dist/antd-mobile.css'; |
|||
|
|||
html { |
|||
font-size: 24px; |
|||
} |
|||
|
|||
body { |
|||
font-size: 1rem; |
|||
} |
|||
|
|||
.text--- { |
|||
text-overflow: ellipsis; |
|||
overflow: hidden; |
|||
word-break: break-all; |
|||
white-space: nowrap; |
|||
} |
Loading…
Reference in new issue