Parcourir la source

feat: 评论、同意、拒绝弹窗

luoyali il y a 1 an
Parent
commit
aad14d3c3d

+ 2 - 0
.env

@@ -4,6 +4,8 @@ VITE_APP_TITLE = aizuda
 # 公共基础路径
 VITE_PUBLIC_PATH = /
 
+VITE_APP_BASE_API = 'http://api.boot.aizuda.com'
+
 # 打包时是否删除 console
 VITE_DROP_CONSOLE = true
 

+ 29 - 0
src/api/common/files.ts

@@ -0,0 +1,29 @@
+import request from '@/utils/request'
+import { AxiosPromise } from 'axios'
+
+const api = {
+	batchGetUrl: '/v1/service-file/batchGetUrl',
+	deleteUrl: '/v1/service-file/removeBind',
+}
+// 批量获取文件访问地址
+function batchGetUrlApi(data: any): AxiosPromise {
+	return request({
+		url: api.batchGetUrl,
+		method: 'get',
+		data
+	})
+}
+
+// 删除文件
+function deleteUrlApi(data: any): AxiosPromise {
+	return request({
+		url: api.deleteUrl,
+		method: 'put',
+		data
+	})
+}
+
+export default {
+	batchGetUrlApi,
+	deleteUrlApi
+}

+ 325 - 0
src/components/FileUpload.vue

@@ -0,0 +1,325 @@
+<template>
+	<div v-loading="isLoading" class="component-upload-image">
+		<el-upload
+			multiple
+			:action="uploadUrl"
+			:http-request="httpRequest"
+			:list-type="listType"
+			:disabled="disabledUpload"
+			:before-upload="handleBeforeUpload"
+			:limit="limit"
+			:on-exceed="handleExceed"
+			name="file"
+			:on-remove="handleRemove"
+			:show-file-list="true"
+			:headers="headers"
+			:file-list="fileList"
+			:on-success="handleUploadSuccess"
+			:auto-upload="true"
+			:accept="accept.join(',')"
+		>
+			<el-button v-show="disabledUpload === false" plain icon="Upload"></el-button>
+			<template v-if="disabledUpload === false" #tip>
+				<!-- 上传提示 -->
+				<el-tooltip v-if="showTip" placement="bottom">
+					<div class="el-upload__tip">
+						<el-icon><QuestionFilled /></el-icon>
+					</div>
+					<template #content>
+						请上传
+						<template v-if="fileSize">
+							大小不超过
+							<b style="color: #f56c6c">{{ fileSize }}MB</b>
+						</template>
+						<template v-if="accept">
+							格式为
+							<b style="color: #f56c6c">{{ accept.join('/') }}</b>
+						</template>
+						的文件
+					</template>
+				</el-tooltip>
+			</template>
+
+			<template #file="{ file }">
+				<div>
+					<el-link type="success" @click="previewFile(file)">{{ file.name }}</el-link>
+					<el-button v-if="disabledUpload === false" text icon="Delete" @click="handleRemove(file)"></el-button>
+				</div>
+			</template>
+		</el-upload>
+
+		<el-dialog v-model="dialogVisible" :title="'预览 (' + dialogTitle + ')'" width="800px" append-to-body destroy-on-close>
+			<iframe :src="dialogUrl" frameborder="0" width="100%" height="500" allowfullscreen></iframe>
+		</el-dialog>
+	</div>
+</template>
+
+<script setup>
+import files from '@/api/common/files'
+// import { getToken } from '@/utils/auth' // 需要获取到token
+import { debounce } from 'lodash-es'
+import { ElMessage } from 'element-plus'
+import request from '@/utils/request'
+import { computed, ref, watch } from 'vue'
+import { QuestionFilled } from '@element-plus/icons-vue'
+
+const props = defineProps({
+	modelValue: {
+		type: [String, Object, Array],
+		default: undefined
+	},
+	// 数量限制
+	limit: {
+		type: Number,
+		default: 5
+	},
+	// 文件所属模块
+	source: {
+		type: String,
+		default: ''
+	},
+	// 大小限制(MB)
+	fileSize: {
+		type: Number,
+		default: 5
+	},
+	// 是否显示提示
+	isShowTip: {
+		type: Boolean,
+		default: true
+	},
+	// 文件列表类型
+	listType: {
+		type: String,
+		default: 'picture'
+	},
+	accept: {
+		type: Array,
+		default: () => {
+			return ['.docx', '.pptx', '.xlsx', '.zip', '.csv', '.pdf']
+		}
+	},
+	preview: {
+		type: Boolean,
+		default: true
+	},
+	disabledUpload: {
+		type: Boolean,
+		default: false
+	},
+	return: {
+		type: String,
+		default: 'array',
+		validator(value) {
+			// 这个值必须与下列字符串中的其中一个相匹配
+			return ['array', 'string'].includes(value)
+		}
+	},
+	loading: {
+		type: Boolean,
+		default: false
+	}
+})
+
+const isLoading = computed({
+	get() {
+		return props.loading
+	},
+	set(val) {
+		console.log('set loading', val)
+		emit('update:loading', val)
+	}
+})
+
+const emit = defineEmits(['success', 'update:modelValue', 'update:loading'])
+const number = ref(0)
+const dialogUrl = ref('')
+const dialogTitle = ref('')
+const dialogVisible = ref(false)
+// const baseUrl = import.meta.env.VITE_APP_BASE_INTERFACE
+const uploadUrl = ref(import.meta.env.VITE_APP_BASE_INTERFACE + '/file/upload') // 上传的服务器地址
+// const headers = ref({ Authorization: 'Bearer ' + getToken() })
+const headers = ref({ Authorization: 'Bearer =====' })
+const fileList = ref([]) // 已上传的
+const showTip = computed(() => props.isShowTip && (props.accept || props.fileSize))
+let multipleCount = 0 //并发上传的数量
+
+watch(
+	() => props.modelValue,
+	async val => {
+		if (val) {
+			// 首先将值转为数组
+			const fileIdList = Array.isArray(val) ? val : props.modelValue.split(',')
+			if (fileIdList.length > 0) {
+				await files.batchGetUrlApi({ fileIdList }).then(res => {
+					fileList.value = res.data
+				})
+			}
+		} else {
+			fileList.value = []
+		}
+	},
+	{ deep: true, immediate: true }
+)
+
+function filterResponse(list) {
+	return list.map(item => {
+		return item.response ? item.response.data : item
+	})
+}
+
+function getFileIds(list) {
+	const ids = list.map(item => item.id)
+	if (props.return === 'string') {
+		return ids.join(',')
+	} else {
+		return ids
+	}
+}
+
+const updateModelValue = debounce(() => {
+	emit('update:modelValue', getFileIds(filterResponse(fileList.value)))
+	emit('success', getFileIds(filterResponse(fileList.value)))
+	isLoading.value = false
+}, 300)
+
+async function handleRemove(file) {
+	isLoading.value = true
+	try {
+		await files.deleteUrlApi({
+			fileIds: [file.id]
+		})
+		number.value--
+		fileList.value = fileList.value.filter(item => item.id !== file.id)
+		updateModelValue()
+		emit('success', {})
+		isLoading.value = false
+	} catch (e) {
+		isLoading.value = false
+		console.log(e);
+	}
+}
+
+function handleUploadSuccess(res) {
+	console.log('handleUploadSuccess', res)
+	isLoading.value = false
+	fileList.value.push(res.data)
+	number.value++
+	multipleCount--
+	if (multipleCount === 0) {
+		updateModelValue()
+	}
+}
+
+function handleBeforeUpload(file) {
+	console.log('handleBeforeUpload', file)
+	isLoading.value = true
+	let isImg = false
+	if (props.accept.length) {
+		let fileExtension = ''
+		if (file.name.toLowerCase().lastIndexOf('.') > -1) {
+			fileExtension = file.name.toLowerCase().slice(file.name.toLowerCase().lastIndexOf('.'))
+		}
+		isImg = props.accept.some(type => {
+			if (file.type.indexOf(type) > -1) return true
+			if (fileExtension && fileExtension.indexOf(type) > -1) return true
+			return false
+		})
+	} else {
+		isImg = file.type.indexOf('image') > -1
+	}
+	if (!isImg) {
+		ElMessage.error(`文件格式不正确, 请上传${props.accept.join('/')}格式文件!`)
+		isLoading.value = false
+		return false
+	}
+	if (props.fileSize) {
+		const isLt = file.size / 1024 / 1024 < props.fileSize
+		if (!isLt) {
+			ElMessage.error(`上传头文件大小不能超过 ${props.fileSize} MB!`)
+			isLoading.value = false
+			return false
+		}
+	}
+	multipleCount++
+}
+
+// 文件个数超出
+function handleExceed() {
+	ElMessage.error(`上传文件数量不能超过 ${props.limit} 个!`)
+}
+
+// 上传失败
+// function handleUploadError() {
+// ElMessage.error(`上传失败`)
+// }
+
+// 预览
+function previewFile(file) {
+	if (!props.preview) return
+
+	const officeFiles = ['.docx', '.pptx', '.xlsx', '.doc', '.ppt', '.xls']
+	const pdfFiles = ['.pdf']
+
+	// 根据file.url确定是否是office文件
+	const isOffice = officeFiles.some(item => file.name.indexOf(item) > -1)
+	const isPdf = pdfFiles.some(item => file.name.indexOf(item) > -1)
+	const isBlob = file.url.startsWith('blob:')
+
+	if (isOffice) {
+		let fileUrl = file.url
+		if (isBlob) {
+			fileUrl = file.response.data.url
+		}
+		dialogUrl.value = 'https://view.officeapps.live.com/op/view.aspx?src=' + encodeURIComponent(fileUrl)
+	} else if (isPdf) {
+		dialogUrl.value = isBlob ? file.response.data.url : file.url
+	} else {
+		// window.location.href = isBlob ? file.response.data.url : file.url
+		window.open(isBlob ? file.response.data.url : file.url)
+		return
+	}
+
+	dialogTitle.value = file.name
+	dialogVisible.value = true
+}
+
+function httpRequest(options) {
+	const data = new FormData()
+	const params = { file: options.file, source: props.source }
+	for (const key in params) {
+		data.append(key, params[key])
+	}
+	return request({
+		url: '/service-file/upload',
+		method: 'post',
+		data
+	})
+}
+</script>
+
+<style scoped lang="scss">
+.upload-file-uploader {
+	margin-bottom: 5px;
+}
+.upload-file-list .el-upload-list__item {
+	border: 1px solid #e4e7ed;
+	line-height: 2;
+	margin-bottom: 10px;
+	position: relative;
+}
+.upload-file-list .ele-upload-list__item-content {
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	color: inherit;
+}
+.ele-upload-list__item-content-action .el-link {
+	margin-right: 10px;
+}
+.el-upload__tip {
+	display: inline-block;
+	margin-left: 10px;
+	line-height: 1.5;
+}
+</style>

+ 91 - 0
src/views/approve/components/consentOrRefuseDialog.vue

@@ -0,0 +1,91 @@
+<template>
+	<!-- 同意 or 拒绝弹窗 -->
+	<el-dialog :visible="modelValue" title="同意审批" width="40%" destroy-on-close>
+		<el-form ref="formRef" v-loading="uploadLoading" element-loading-text="图片上传中..." :model="form" label-width="120px">
+			<el-form-item label="评论" prop="review" :rules="[{ required: true, message: '评论内容不能为空' }]">
+				<el-input v-model="form.review" type="textarea" placeholder="请输入内容" maxlength="64" show-word-limit> </el-input>
+			</el-form-item>
+			<el-form-item prop="attachment" label="附件" class="example-img-box">
+				<!--'.docx', '.doc', '.pptx', '.ppt', '.xlsx', '.xls', '.zip', '.csv', '.pdf', '.png', '.jpg'  因前端不支持图片以外格式,所以注释 -->
+				<FileUpload
+					v-model="form.attachment"
+					source="project"
+					return="array"
+					:limit="5"
+					:file-size="10"
+					:accept="['.png', '.jpg']"
+					@success="clearValidate"
+				/>
+			</el-form-item>
+		</el-form>
+		<template #footer>
+			<span class="dialog-footer">
+				<el-button :loading="btnDisabled" @click="closeDialog">取 消</el-button>
+				<el-button type="primary" :loading="btnDisabled" :disabled="uploadLoading" @click="submitForm">确 定</el-button>
+			</span>
+		</template>
+	</el-dialog>
+</template>
+
+<script setup>
+import { reactive, ref } from 'vue'
+import FileUpload from '@/components/FileUpload.vue'
+
+const props = defineProps({
+	modelValue: {
+		type: Boolean,
+		default: false
+	},
+	showOrderDesc: {
+		type: Boolean,
+		default: true
+	},
+	// 1-审核 2-复审 3-审核,复审 ,第一版默认审核
+	linkType: {
+		type: String,
+		default: '1'
+	}
+})
+const btnDisabled = ref(false)
+const form = reactive({
+	review: '',
+	attachment: []
+})
+const formRef = ref(null)
+const uploadLoading = ref(false)
+
+const emit = defineEmits(['update:modelValue', 'confirm'])
+
+const submitForm = () => {
+	btnDisabled.value = true
+	const formData = { ...form }
+	formRef.value
+		.validate()
+		.then(valid => {
+			if (valid) {
+				emit('confirm', formData)
+				btnDisabled.value = false
+			}
+		})
+		.catch(err => {
+			console.error('挂起订单表单拦截', err)
+			btnDisabled.value = false
+		})
+}
+
+const closeDialog = () => {
+	$myEmit('update:modelValue', false)
+}
+
+const clearValidate = () => {}
+</script>
+
+<style scoped>
+.example-img-box {
+	width: 100% !important;
+}
+
+.example-img-box .el-form-item__content {
+	display: block;
+}
+</style>

+ 91 - 0
src/views/approve/components/reviewDialog.vue

@@ -0,0 +1,91 @@
+<template>
+	<!-- 评论弹窗 -->
+	<el-dialog :visible="modelValue" title="评论内容" width="40%" destroy-on-close>
+		<el-form ref="formRef" v-loading="uploadLoading" element-loading-text="图片上传中..." :model="form" label-width="120px">
+			<el-form-item label="评论" prop="review" :rules="[{ required: true, message: '评论内容不能为空' }]">
+				<el-input v-model="form.review" type="textarea" placeholder="请输入内容" maxlength="64" show-word-limit> </el-input>
+			</el-form-item>
+			<el-form-item prop="attachment" label="附件" class="example-img-box">
+				<!--'.docx', '.doc', '.pptx', '.ppt', '.xlsx', '.xls', '.zip', '.csv', '.pdf', '.png', '.jpg'  因前端不支持图片以外格式,所以注释 -->
+				<FileUpload
+					v-model="form.attachment"
+					source="project"
+					return="array"
+					:limit="5"
+					:file-size="10"
+					:accept="['.png', '.jpg']"
+					@success="clearValidate"
+				/>
+			</el-form-item>
+		</el-form>
+		<template #footer>
+			<span class="dialog-footer">
+				<el-button :loading="btnDisabled" @click="closeDialog">取 消</el-button>
+				<el-button type="primary" :loading="btnDisabled" :disabled="uploadLoading" @click="submitForm">确 定</el-button>
+			</span>
+		</template>
+	</el-dialog>
+</template>
+
+<script setup>
+import { reactive, ref } from 'vue'
+import FileUpload from '@/components/FileUpload.vue'
+
+const props = defineProps({
+	modelValue: {
+		type: Boolean,
+		default: false
+	},
+	showOrderDesc: {
+		type: Boolean,
+		default: true
+	},
+	// 1-审核 2-复审 3-审核,复审 ,第一版默认审核
+	linkType: {
+		type: String,
+		default: '1'
+	}
+})
+const btnDisabled = ref(false)
+const form = reactive({
+	review: '',
+	attachment: []
+})
+const formRef = ref(null)
+const uploadLoading = ref(false)
+
+const emit = defineEmits(['update:modelValue', 'confirm'])
+
+const submitForm = () => {
+	btnDisabled.value = true
+	const formData = { ...form }
+	formRef.value
+		.validate()
+		.then(valid => {
+			if (valid) {
+				emit('confirm', formData)
+				btnDisabled.value = false
+			}
+		})
+		.catch(err => {
+			console.error('挂起订单表单拦截', err)
+			btnDisabled.value = false
+		})
+}
+
+const closeDialog = () => {
+	$myEmit('update:modelValue', false)
+}
+
+const clearValidate = () => {}
+</script>
+
+<style scoped>
+.example-img-box {
+	width: 100% !important;
+}
+
+.example-img-box .el-form-item__content {
+	display: block;
+}
+</style>

+ 5 - 38
src/views/approve/pendingClaim/detail.vue

@@ -93,23 +93,14 @@
 				<el-button :icon="ChatLineSquare" @click="openComment">评论</el-button>
 			</div>
 		</div>
-
-		<LeFormConfigDialog
-			v-if="visible"
-			ref="dialogCommentRef"
-			v-model="visible"
-			title="评论审批"
-			width="600px"
-			:form-data="activeData"
-			:form-options="formOptions"
-			@submit="submitHandler"
-		/>
 	</el-drawer>
+	<review-dialog v-if="reviewVisible" v-model="reviewVisible"></review-dialog>
 </template>
 
 <script setup>
 import { computed, ref } from 'vue'
 import { ChatLineSquare, Check } from '@element-plus/icons-vue'
+import ReviewDialog from '../components/reviewDialog'
 
 const direction = ref('rtl')
 const myProps = defineProps({
@@ -123,8 +114,7 @@ const myProps = defineProps({
 	}
 })
 
-const visible = ref(false) // 弹窗显示隐藏
-const activeData = ref({})
+const reviewVisible = ref(false)
 
 // 同步值
 const $myEmit = defineEmits(['update:modelValue', 'successFn'])
@@ -148,32 +138,9 @@ const visibleDialog = computed({
 	}
 })
 
-const formsDialog = [
-	{
-		prop: 'username',
-		label: '评论',
-		itemType: 'input',
-		placeholder: '请输入评论',
-		rules: [{ required: true, message: '请输入评论', trigger: 'blur' }]
-	}
-]
-
-const formOptions = computed(() => {
-	return {
-		forms: formsDialog,
-		labelWidth: 120,
-		span: 30,
-		formConfig: {
-			showCancelBtn: true,
-			submitLoading: false
-		}
-	}
-})
-
-const submitHandler = () => {}
-
 const openComment = () => {
-  visible.value = !visible.value
+	debugger
+	reviewVisible.value = !reviewVisible.value
 }
 </script>