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