Commit de7e2f04 authored by 郭铭瑶's avatar 郭铭瑶 🤘

上传功能完成

parent 7ef6e681
...@@ -69,6 +69,10 @@ const themeOverrides: GlobalThemeOverrides = { ...@@ -69,6 +69,10 @@ const themeOverrides: GlobalThemeOverrides = {
Card: { Card: {
borderRadius: '.06rem', borderRadius: '.06rem',
}, },
Upload: {
itemTextColorSuccess: '#3D7FE9',
itemTextColorError: '#E59B00',
},
Tag: { Tag: {
borderRadius: '.04rem', borderRadius: '.04rem',
colorPrimary: '#FCF4F5', colorPrimary: '#FCF4F5',
......
...@@ -10,12 +10,13 @@ const getCookie = (name: string): string | null => { ...@@ -10,12 +10,13 @@ const getCookie = (name: string): string | null => {
switch (process.env.NODE_ENV) { switch (process.env.NODE_ENV) {
case 'production': case 'production':
BASE_URL = 'https://survey.maicedata.com/api/data/' BASE_URL = 'https://www.maicedata.com/collector/data/' // 生产环境
TOKEN = LZString.decompressFromEncodedURIComponent( TOKEN = LZString.decompressFromEncodedURIComponent(
getCookie('__DM_TOKEN__') || window.localStorage.getItem('dm_token_'), getCookie('__DM_TOKEN__') || window.localStorage.getItem('dm_token_'),
) )
break break
default: default:
// vite.config 代理 https://survey.maicedata.com/api/data/
BASE_URL = '/api' BASE_URL = '/api'
TOKEN = '91e315a9-b2a8-4950-97fa-9dbf84a230d6' TOKEN = '91e315a9-b2a8-4950-97fa-9dbf84a230d6'
} }
...@@ -35,5 +36,7 @@ export default { ...@@ -35,5 +36,7 @@ export default {
ATTACHMENT: '988fc63e-fa55-4729-851d-24c4355213f2', ATTACHMENT: '988fc63e-fa55-4729-851d-24c4355213f2',
TAG: '54344e11-c0d3-40d5-8f99-771066249328', TAG: '54344e11-c0d3-40d5-8f99-771066249328',
GET_AUTH: '988fc63e-fa55-4729-851d-24c4355213f2/storage/grant', GET_AUTH: '988fc63e-fa55-4729-851d-24c4355213f2/storage/grant',
GET_USER_ID: '/auth/user/bytoken', GET_USER_ID: 'https://www.maicedata.com/auth/user/bytoken',
USER: '3625616b-7f2c-449a-8306-101aac7b8b97',
ORG: 'b47ddc9a-0aa7-45a8-b1b6-6064f888d537',
} }
...@@ -6,7 +6,7 @@ import { api } from '@/ajax' ...@@ -6,7 +6,7 @@ import { api } from '@/ajax'
export default async function useAliOss(file: any) { export default async function useAliOss(file: any) {
const auth = (await useFetchAuth())?.token const auth = (await useFetchAuth())?.token
const client = new OSS({ const client = new OSS({
region: auth.region, region: 'oss-' + auth.region,
accessKeyId: auth.access_key_id, accessKeyId: auth.access_key_id,
accessKeySecret: auth.access_key_secret, accessKeySecret: auth.access_key_secret,
bucket: auth.bucket, bucket: auth.bucket,
...@@ -19,7 +19,7 @@ export default async function useAliOss(file: any) { ...@@ -19,7 +19,7 @@ export default async function useAliOss(file: any) {
fr.readAsArrayBuffer(file) fr.readAsArrayBuffer(file)
fr.onload = async (e) => { fr.onload = async (e) => {
const buffer = new OSS.Buffer(e.target && e.target.result) const buffer = new OSS.Buffer(e.target && e.target.result)
const key = `collector/${api.ACTIVITY}/huamu_${md5(buffer)}_${filename}` const key = `collector/${api.ATTACHMENT}/huamu_${md5(buffer)}_${filename}`
const res = await client.put(key, buffer) const res = await client.put(key, buffer)
resolve(res.url) resolve(res.url)
} }
......
...@@ -6,6 +6,7 @@ import { ...@@ -6,6 +6,7 @@ import {
HeadingLevel, HeadingLevel,
TextRun, TextRun,
ImageRun, ImageRun,
ExternalHyperlink,
} from 'docx' } from 'docx'
function createHeading(text: string): Paragraph { function createHeading(text: string): Paragraph {
...@@ -26,9 +27,9 @@ function createText(text: string): Paragraph { ...@@ -26,9 +27,9 @@ function createText(text: string): Paragraph {
}) })
} }
async function createImage(key: string, urls: string[]) { async function createImage(key: string, items: any[]) {
const blobs = await Promise.all( const blobs = await Promise.all(
urls.map(async (url) => await fetch(url).then((r: any) => r.blob())), items.map(async (item) => await fetch(item.url).then((r: any) => r.blob())),
) )
return new Paragraph({ return new Paragraph({
children: [ children: [
...@@ -46,6 +47,22 @@ async function createImage(key: string, urls: string[]) { ...@@ -46,6 +47,22 @@ async function createImage(key: string, urls: string[]) {
], ],
}) })
} }
function createLink(key: string, items: any[]) {
return new Paragraph({
children: [
new TextRun({ text: `${key}:` }),
...items.map(
(item: any) =>
new ExternalHyperlink({
link: item.url,
child: new TextRun({
text: decodeURIComponent(item.url.split('_').pop() || '未命名'),
}),
}),
),
],
})
}
async function createDocument(data: any[], labelKey: string) { async function createDocument(data: any[], labelKey: string) {
function arrayToString(e: unknown) { function arrayToString(e: unknown) {
...@@ -65,6 +82,7 @@ async function createDocument(data: any[], labelKey: string) { ...@@ -65,6 +82,7 @@ async function createDocument(data: any[], labelKey: string) {
if (key.startsWith('_')) return createText('') if (key.startsWith('_')) return createText('')
if (key.includes('照片')) if (key.includes('照片'))
return await createImage(key, item[key]) return await createImage(key, item[key])
if (key.includes('文件')) return createLink(key, item[key])
return createText(`${key}${arrayToString(item[key])}`) return createText(`${key}${arrayToString(item[key])}`)
}), }),
)), )),
......
...@@ -202,7 +202,6 @@ const getRooms = async (query: string) => { ...@@ -202,7 +202,6 @@ const getRooms = async (query: string) => {
) )
} }
const getFloors = async (data: any) => { const getFloors = async (data: any) => {
console.log('123', data)
return Promise.all( return Promise.all(
( (
await useFetchRoom({ await useFetchRoom({
......
...@@ -59,7 +59,6 @@ const props = defineProps({ ...@@ -59,7 +59,6 @@ const props = defineProps({
const trans = (val: string) => { const trans = (val: string) => {
return (val && val.replace(/(\w{3})\w*(\w{4})/, '$1******$2')) || '无' return (val && val.replace(/(\w{3})\w*(\w{4})/, '$1******$2')) || '无'
} }
console.log(dayjs().diff('1999-09-12', 'year'))
const tags = computed(() => { const tags = computed(() => {
const result: { type?: string; name?: string }[] = [] const result: { type?: string; name?: string }[] = []
const marker: string[] = props.data['标签'] || [] const marker: string[] = props.data['标签'] || []
......
...@@ -165,6 +165,7 @@ ...@@ -165,6 +165,7 @@
:span="24" :span="24"
path="attachment" path="attachment"
class="no-label" class="no-label"
:style="mode === 'view' ? 'margin-top:-0.9rem' : null"
> >
<n-upload <n-upload
:default-file-list="memberData.attachment || []" :default-file-list="memberData.attachment || []"
...@@ -173,7 +174,7 @@ ...@@ -173,7 +174,7 @@
:on-change="(e) => onChange(e, 'memberData.attachment')" :on-change="(e) => onChange(e, 'memberData.attachment')"
:show-remove-button="mode !== 'view'" :show-remove-button="mode !== 'view'"
> >
<n-button> 上传附件 </n-button> <n-button v-if="mode !== 'view'"> 上传附件 </n-button>
</n-upload> </n-upload>
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi <n-form-item-gi
...@@ -370,7 +371,7 @@ watch( ...@@ -370,7 +371,7 @@ watch(
detailData.value = {} detailData.value = {}
return return
} }
getPersonNum(data['党组织名称']) getOrgInfo(data['党组织名称'])
basicData.value = { basicData.value = {
name: data['活动名称'], name: data['活动名称'],
type: data['标签类别'], type: data['标签类别'],
...@@ -385,11 +386,21 @@ watch( ...@@ -385,11 +386,21 @@ watch(
count: data['实际参与人数'], count: data['实际参与人数'],
excludeCount: data['不计入参与活动党员人数'], excludeCount: data['不计入参与活动党员人数'],
fileType, fileType,
attachment: fileType === 'file' ? data['签到表文件'] : [], attachment:
fileType === 'file'
? data['签到表文件'].map((url: string, i: number) => ({
id: i,
url,
name: decodeURIComponent(url.split('_').pop() || '未命名'),
status: 'finished',
}))
: [],
attachmentPhoto: attachmentPhoto:
fileType === 'photo' fileType === 'photo'
? (data['签到表照片'] || []).map((url: string) => ({ ? (data['签到表照片'] || []).map((url: string, i: number) => ({
id: i,
url, url,
name: decodeURIComponent(url.split('_').pop() || '未命名'),
status: 'finished', status: 'finished',
})) }))
: [], : [],
...@@ -403,14 +414,18 @@ watch( ...@@ -403,14 +414,18 @@ watch(
attachment: describeType === 'text' ? data['活动内容描述'] : '', attachment: describeType === 'text' ? data['活动内容描述'] : '',
attachmentPhoto: attachmentPhoto:
describeType === 'photo' describeType === 'photo'
? (data['台账记录照片'] || []).map((url: string) => ({ ? (data['台账记录照片'] || []).map((url: string, i: number) => ({
id: i,
url, url,
name: decodeURIComponent(url.split('_').pop() || '未命名'),
status: 'finished', status: 'finished',
})) }))
: [], : [],
photoList: photoList:
(data['活动照片'] || []).map((url: string) => ({ (data['活动照片'] || []).map((url: string, i: number) => ({
id: i,
url, url,
name: decodeURIComponent(url.split('_').pop() || '未命名'),
status: 'finished', status: 'finished',
})) || [], })) || [],
} }
...@@ -474,15 +489,75 @@ const rules: FormRules = { ...@@ -474,15 +489,75 @@ const rules: FormRules = {
trigger: ['blur', 'input'], trigger: ['blur', 'input'],
}, },
], ],
fileType: { fileType: [
required: true, {
trigger: ['blur', 'change'], required: true,
message: '请选择上传类型', trigger: ['blur', 'change'],
}, message: '请选择上传类型',
describeType: { },
{
validator: (_, val) => {
if (val !== 'file') return true
if (
!memberData.value.attachment ||
memberData.value.attachment.length === 0
) {
return false
}
return true
},
trigger: ['blur', 'change'],
message: '请上传附件',
},
{
validator: (_, val) => {
if (val !== 'photo') return true
if (
!memberData.value.attachmentPhoto ||
memberData.value.attachmentPhoto.length === 0
) {
return false
}
return true
},
trigger: ['blur', 'change'],
message: '请上传签到照片',
},
],
describeType: [
{
required: true,
trigger: ['blur', 'change'],
message: '请选择描述类型',
},
{
validator: (_, val) => {
if (val !== 'text') return true
if (!detailData.value.attachment) return false
return true
},
trigger: ['blur', 'change'],
message: '请输入活动内容描述',
},
{
validator: (_, val) => {
if (val !== 'photo') return true
if (
!detailData.value.attachmentPhoto ||
detailData.value.attachmentPhoto.length === 0
)
return false
return true
},
trigger: ['blur', 'change'],
message: '请上传描述照片',
},
],
photoList: {
type: 'array',
required: true, required: true,
trigger: ['blur', 'change'], trigger: ['blur', 'change'],
message: '请选择描述类型', message: '请上传活动照片',
}, },
} }
...@@ -511,6 +586,8 @@ const submit = () => { ...@@ -511,6 +586,8 @@ const submit = () => {
实际参与人数: count, 实际参与人数: count,
不计入参与活动党员人数: excludeCount, 不计入参与活动党员人数: excludeCount,
活动照片: photoList?.map((item: any) => item.url) || [], 活动照片: photoList?.map((item: any) => item.url) || [],
上海2000经度: location.value[0],
上海2000纬度: location.value[1],
出席率: 出席率:
totalPerson.value === excludeCount totalPerson.value === excludeCount
? 0 ? 0
...@@ -580,23 +657,79 @@ function getPercent(val: number) { ...@@ -580,23 +657,79 @@ function getPercent(val: number) {
async function onChange(options: any, type: string) { async function onChange(options: any, type: string) {
console.log('change', options, type) console.log('change', options, type)
if (!options || !options.fileList || options.fileList.length === 0) return if (!options) return
const urls = await Promise.all( if (!options.event) {
options.fileList.map(async (item: any) => await useAliOss(item.file)), const { url } = options.file
) switch (type) {
case 'memberData.attachment':
memberData.value.attachment.splice(
memberData.value.attachment.findIndex((e: any) => e?.url === url),
1,
)
break
case 'memberData.attachmentPhoto':
memberData.value.attachmentPhoto.splice(
memberData.value.attachmentPhoto.findIndex(
(e: any) => e?.url === url,
),
1,
)
break
case 'detailData.attachmentPhoto':
detailData.value.attachmentPhoto.splice(
detailData.value.attachmentPhoto.findIndex(
(e: any) => e?.url === url,
),
1,
)
break
case 'detailData.photoList':
detailData.value.photoList.splice(
detailData.value.photoList.findIndex((e: any) => e?.url === url),
1,
)
break
default:
break
}
return
}
if (!options.file) return
const url = { url: await useAliOss(options.file.file) }
switch (type) { switch (type) {
case 'memberData.attachment': case 'memberData.attachment':
memberData.value.attachment = urls if (!memberData.value.attachment) {
memberData.value.attachment = []
memberData.value.attachment.push(url)
} else {
memberData.value.attachment.push(url)
}
break break
case 'memberData.attachmentPhoto': case 'memberData.attachmentPhoto':
memberData.value.attachmentPhoto = urls if (!memberData.value.attachmentPhoto) {
memberData.value.attachmentPhoto = []
memberData.value.attachmentPhoto.push(url)
} else {
memberData.value.attachmentPhoto.push(url)
}
break break
case 'detailData.attachmentPhoto': case 'detailData.attachmentPhoto':
detailData.value.attachmentPhoto = urls if (!detailData.value.attachmentPhoto) {
detailData.value.attachmentPhoto = []
detailData.value.attachmentPhoto.push(url)
} else {
detailData.value.attachmentPhoto.push(url)
}
break break
case 'detailData.photoList': case 'detailData.photoList':
detailData.value.photoList = urls if (!detailData.value.photoList) {
detailData.value.photoList = []
detailData.value.photoList.push(url)
} else {
detailData.value.photoList.push(url)
}
break break
default: default:
break break
...@@ -604,22 +737,24 @@ async function onChange(options: any, type: string) { ...@@ -604,22 +737,24 @@ async function onChange(options: any, type: string) {
} }
const totalPerson = ref(0) const totalPerson = ref(0)
async function getPersonNum(name: string) { const location = ref<number[]>([])
if (!name) return 0 async function getOrgInfo(name: string) {
return ( if (!name) return
( const res = (
await useFetchOrg({ await useFetchOrg({
keys: '党员数量', keys: '党员数量,上海2000经度,上海2000纬度',
q: `paths @ "党组织名称" && string == "${name}"`, q: `paths @ "党组织名称" && string == "${name}"`,
}) })
)?.[0]?.['党员数量'] || 0 )?.[0]
) totalPerson.value = res?.['党员数量'] || 0
location.value = [res?.['上海2000经度'] || 0, res?.['上海2000纬度'] || 0]
} }
watch( watch(
() => basicData.value.orgName, () => basicData.value.orgName,
async (name) => { (name) => {
totalPerson.value = await getPersonNum(name) getOrgInfo(name)
}, },
{ immediate: true },
) )
function checkMemberCount(val: number) { function checkMemberCount(val: number) {
...@@ -627,8 +762,12 @@ function checkMemberCount(val: number) { ...@@ -627,8 +762,12 @@ function checkMemberCount(val: number) {
} }
const handleExport = async () => { const handleExport = async () => {
const { name, type, orgName, date, address } = basicData.value const { name, type, orgName, date, address } = basicData.value
const { count, excludeCount } = memberData.value const { count, excludeCount, attachment, attachmentPhoto } = memberData.value
const { attachment } = detailData.value const {
attachment: detailAttachment,
attachmentPhoto: detailAttachmentPhoto,
photoList,
} = detailData.value
const data = [ const data = [
{ {
活动名称: name, 活动名称: name,
...@@ -648,14 +787,23 @@ const handleExport = async () => { ...@@ -648,14 +787,23 @@ const handleExport = async () => {
) + '%', ) + '%',
}, },
{ {
活动内容描述: attachment, 活动内容描述: detailAttachment,
// TODO 测试图片 活动照片: photoList,
活动照片: [
'https://raw.githubusercontent.com/dolanmiu/docx/master/demo/images/cat.jpg',
'https://raw.githubusercontent.com/dolanmiu/docx/master/demo/images/cat.jpg',
],
}, },
] ]
if (attachment && attachment.length > 0) {
data[1]['签到表文件'] = attachment
}
if (attachmentPhoto && attachmentPhoto.length > 0) {
data[1]['签到表照片'] = attachmentPhoto
}
if (detailAttachment) {
data[2]['活动内容描述'] = detailAttachment
}
if (detailAttachmentPhoto && detailAttachmentPhoto.length > 0) {
data[2]['台账记录照片'] = detailAttachmentPhoto
}
await useExportFile( await useExportFile(
[ [
{ {
...@@ -742,5 +890,5 @@ const handleExport = async () => { ...@@ -742,5 +890,5 @@ const handleExport = async () => {
.n-upload__trigger.n-upload__trigger--image-card .n-upload__trigger.n-upload__trigger--image-card
display none !important display none !important
.no-label .no-label
margin-top -0.3rem margin-top -0.2rem
</style> </style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment