init
This commit is contained in:
parent
b8eebd1d17
commit
30b524a25d
|
|
@ -0,0 +1,104 @@
|
||||||
|
<template>
|
||||||
|
<span class="headerAvatar">
|
||||||
|
<template v-if="picType === 'avatar'">
|
||||||
|
<el-avatar
|
||||||
|
v-if="userStore.userInfo.headerImg"
|
||||||
|
:size="30"
|
||||||
|
:src="avatar"
|
||||||
|
/>
|
||||||
|
<el-avatar
|
||||||
|
v-else
|
||||||
|
:size="30"
|
||||||
|
:src="noAvatar"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template v-if="picType === 'img'">
|
||||||
|
<img
|
||||||
|
v-if="userStore.userInfo.headerImg"
|
||||||
|
:src="avatar"
|
||||||
|
class="avatar"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
v-else
|
||||||
|
:src="noAvatar"
|
||||||
|
class="avatar"
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<template v-if="picType === 'file'">
|
||||||
|
<el-image
|
||||||
|
:src="file"
|
||||||
|
class="file"
|
||||||
|
:preview-src-list="previewSrcList"
|
||||||
|
:preview-teleported="true"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import noAvatarPng from '@/assets/noBody.png'
|
||||||
|
import { useUserStore } from '@/pinia/modules/user'
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'CustomPic'
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
picType: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'avatar'
|
||||||
|
},
|
||||||
|
picSrc: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
preview: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const path = ref(import.meta.env.VITE_BASE_API + '/')
|
||||||
|
const noAvatar = ref(noAvatarPng)
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
const avatar = computed(() => {
|
||||||
|
if (props.picSrc === '') {
|
||||||
|
if (userStore.userInfo.headerImg !== '' && userStore.userInfo.headerImg.slice(0, 4) === 'http') {
|
||||||
|
return userStore.userInfo.headerImg
|
||||||
|
}
|
||||||
|
return path.value + userStore.userInfo.headerImg
|
||||||
|
} else {
|
||||||
|
if (props.picSrc !== '' && props.picSrc.slice(0, 4) === 'http') {
|
||||||
|
return props.picSrc
|
||||||
|
}
|
||||||
|
return path.value + props.picSrc
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const file = computed(() => {
|
||||||
|
if (props.picSrc && props.picSrc.slice(0, 4) !== 'http') {
|
||||||
|
return path.value + props.picSrc
|
||||||
|
}
|
||||||
|
return props.picSrc
|
||||||
|
})
|
||||||
|
const previewSrcList = computed(() => props.preview ? [file.value] : [])
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.headerAvatar{
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
.file{
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
<template>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="download"
|
||||||
|
@click="exportExcelFunc"
|
||||||
|
>导出</el-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
templateId: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
condition: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
limit: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
offset: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
order: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const exportExcelFunc = async() => {
|
||||||
|
if (props.templateId === '') {
|
||||||
|
ElMessage.error('组件未设置模板ID')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const baseUrl = import.meta.env.VITE_BASE_API
|
||||||
|
const paramsCopy = JSON.parse(JSON.stringify(props.condition))
|
||||||
|
if (props.limit) {
|
||||||
|
paramsCopy.limit = props.limit
|
||||||
|
}
|
||||||
|
if (props.offset) {
|
||||||
|
paramsCopy.offset = props.offset
|
||||||
|
}
|
||||||
|
if (props.order) {
|
||||||
|
paramsCopy.order = props.order
|
||||||
|
}
|
||||||
|
const params = Object.entries(paramsCopy)
|
||||||
|
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
|
||||||
|
.join('&')
|
||||||
|
const url = `${baseUrl}/sysExportTemplate/exportExcel?templateID=${props.templateId}${params ? '&' + params : ''}`
|
||||||
|
|
||||||
|
window.open(url, '_blank')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
<template>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="download"
|
||||||
|
@click="exportTemplate"
|
||||||
|
>下载模板</el-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
templateId: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const exportTemplate = async() => {
|
||||||
|
if (props.templateId === '') {
|
||||||
|
ElMessage.error('组件未设置模板ID')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const baseUrl = import.meta.env.VITE_BASE_API
|
||||||
|
const url = `${baseUrl}/sysExportTemplate/exportTemplate?templateID=${props.templateId}`
|
||||||
|
window.open(url, '_blank')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
<template>
|
||||||
|
<el-upload
|
||||||
|
:action="url"
|
||||||
|
:show-file-list="false"
|
||||||
|
:on-success="handleSuccess"
|
||||||
|
:multiple="false"
|
||||||
|
>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="upload"
|
||||||
|
>导入</el-button>
|
||||||
|
</el-upload>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const baseUrl = import.meta.env.VITE_BASE_API
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
templateId: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['on-success'])
|
||||||
|
|
||||||
|
const url = `${baseUrl}/sysExportTemplate/importExcel?templateID=${props.templateId}`
|
||||||
|
|
||||||
|
const handleSuccess = (res) => {
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage.success('导入成功')
|
||||||
|
emit('on-success')
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
<template>
|
||||||
|
<vue-office-docx
|
||||||
|
:src="docx"
|
||||||
|
@rendered="rendered"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Docx'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script setup>
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
|
||||||
|
// 引入VueOfficeDocx组件
|
||||||
|
import VueOfficeDocx from '@vue-office/docx'
|
||||||
|
// 引入相关样式
|
||||||
|
import '@vue-office/docx/lib/index.css'
|
||||||
|
|
||||||
|
const model = defineModel({
|
||||||
|
type: String,
|
||||||
|
})
|
||||||
|
|
||||||
|
const docx = ref(null)
|
||||||
|
watch(
|
||||||
|
() => model,
|
||||||
|
value => { docx.value = value },
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
const rendered = () => {
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
<template>
|
||||||
|
<VueOfficeExcel :src="excel" @rendered="renderedHandler" @error="errorHandler" style="height: 100vh;width: 100vh"/>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Excel'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script setup>
|
||||||
|
//引入VueOfficeExcel组件
|
||||||
|
import VueOfficeExcel from '@vue-office/excel'
|
||||||
|
//引入相关样式
|
||||||
|
import '@vue-office/excel/lib/index.css'
|
||||||
|
import {ref, watch} from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
default: () => ""
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const excel = ref('')
|
||||||
|
watch(() => props.modelValue, val => excel.value = val, {immediate: true})
|
||||||
|
const renderedHandler = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
const errorHandler = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
<template>
|
||||||
|
<div class="border border-solid border-gray-100 h-full w-full">
|
||||||
|
<el-row>
|
||||||
|
<div v-if="ext==='docx'">
|
||||||
|
<Docx v-model="fullFileURL" />
|
||||||
|
</div>
|
||||||
|
<div v-else-if="ext==='pdf'">
|
||||||
|
<Pdf v-model="fullFileURL" />
|
||||||
|
</div>
|
||||||
|
<div v-else-if="ext==='xlsx'">
|
||||||
|
<Excel v-model="fullFileURL" />
|
||||||
|
</div>
|
||||||
|
<div v-else-if="ext==='image'">
|
||||||
|
<el-image
|
||||||
|
:src="fullFileURL"
|
||||||
|
lazy
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Office'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script setup>
|
||||||
|
import { ref, watch, computed } from 'vue'
|
||||||
|
import Docx from '@/components/office/docx.vue'
|
||||||
|
import Pdf from '@/components/office/pdf.vue'
|
||||||
|
import Excel from '@/components/office/excel.vue'
|
||||||
|
|
||||||
|
const path = ref(import.meta.env.VITE_BASE_API)
|
||||||
|
|
||||||
|
const model = defineModel({ type: String})
|
||||||
|
|
||||||
|
const fileUrl = ref('')
|
||||||
|
const ext = ref('')
|
||||||
|
watch(
|
||||||
|
() => model,
|
||||||
|
val => {
|
||||||
|
fileUrl.value = val
|
||||||
|
const fileExt = val.split('.')[1] || ''
|
||||||
|
const image = ['png', 'jpg', 'jpeg', 'gif']
|
||||||
|
ext.value = image.includes(fileExt) ? 'image' : fileExt
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
const fullFileURL = computed(() => {
|
||||||
|
return path.value + '/' + fileUrl.value
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
<template>
|
||||||
|
<vue-office-pdf
|
||||||
|
|
||||||
|
:src="pdf"
|
||||||
|
@rendered="renderedHandler"
|
||||||
|
@error="errorHandler"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "Pdf"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script setup>
|
||||||
|
import {ref, watch} from "vue"
|
||||||
|
|
||||||
|
//引入VueOfficeDocx组件
|
||||||
|
import VueOfficePdf from "@vue-office/pdf";
|
||||||
|
//引入相关样式
|
||||||
|
import '@vue-office/docx/lib/index.css'
|
||||||
|
console.log("pdf===>")
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
default: () => ""
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const pdf = ref(null)
|
||||||
|
watch(() => props.modelValue, val => pdf.value = val, {immediate: true})
|
||||||
|
const renderedHandler = () => {
|
||||||
|
console.log("pdf 加载成功")
|
||||||
|
}
|
||||||
|
const errorHandler = () => {
|
||||||
|
console.log("pdf 错误")
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
<template>
|
||||||
|
<div class="border border-solid border-gray-100 h-full">
|
||||||
|
<Toolbar
|
||||||
|
:editor="editorRef"
|
||||||
|
:default-config="toolbarConfig"
|
||||||
|
mode="default"
|
||||||
|
/>
|
||||||
|
<Editor
|
||||||
|
v-model="valueHtml"
|
||||||
|
class="overflow-y-hidden mt-0.5"
|
||||||
|
style="height: 18rem;"
|
||||||
|
:default-config="editorConfig"
|
||||||
|
mode="default"
|
||||||
|
@onCreated="handleCreated"
|
||||||
|
@onChange="change"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
import '@wangeditor/editor/dist/css/style.css' // 引入 css
|
||||||
|
|
||||||
|
const basePath = import.meta.env.VITE_BASE_API
|
||||||
|
|
||||||
|
import { onBeforeUnmount, ref, shallowRef, watch } from 'vue'
|
||||||
|
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
||||||
|
|
||||||
|
import { useUserStore } from '@/pinia/modules/user'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { getUrl } from '@/utils/image'
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
const emits = defineEmits(['change', 'update:modelValue'])
|
||||||
|
|
||||||
|
const change = (editor) => {
|
||||||
|
emits('change', editor)
|
||||||
|
emits('update:modelValue', valueHtml.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const editorRef = shallowRef()
|
||||||
|
const valueHtml = ref('')
|
||||||
|
|
||||||
|
const toolbarConfig = {}
|
||||||
|
const editorConfig = {
|
||||||
|
placeholder: '请输入内容...',
|
||||||
|
MENU_CONF: {}
|
||||||
|
}
|
||||||
|
editorConfig.MENU_CONF['uploadImage'] = {
|
||||||
|
fieldName: 'file',
|
||||||
|
server: basePath + '/fileUploadAndDownload/upload?noSave=1',
|
||||||
|
customInsert(res, insertFn) {
|
||||||
|
if (res.code === 0) {
|
||||||
|
const urlPath = getUrl(res.data.file.url)
|
||||||
|
insertFn(urlPath, res.data.file.name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ElMessage.error(res.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件销毁时,也及时销毁编辑器
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
const editor = editorRef.value
|
||||||
|
if (editor == null) return
|
||||||
|
editor.destroy()
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleCreated = (editor) => {
|
||||||
|
editorRef.value = editor
|
||||||
|
valueHtml.value = props.modelValue
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.modelValue, () => {
|
||||||
|
valueHtml.value = props.modelValue
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
<template>
|
||||||
|
<div class="border border-solid border-gray-100 h-full">
|
||||||
|
<Editor
|
||||||
|
v-model="valueHtml"
|
||||||
|
class="overflow-y-hidden mt-0.5"
|
||||||
|
:default-config="editorConfig"
|
||||||
|
mode="default"
|
||||||
|
@onCreated="handleCreated"
|
||||||
|
@onChange="change"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
import '@wangeditor/editor/dist/css/style.css' // 引入 css
|
||||||
|
|
||||||
|
import { onBeforeUnmount, ref, shallowRef, watch } from 'vue'
|
||||||
|
import { Editor } from '@wangeditor/editor-for-vue'
|
||||||
|
|
||||||
|
import { useUserStore } from '@/pinia/modules/user'
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
const emits = defineEmits(['change', 'update:modelValue'])
|
||||||
|
const editorConfig = ref({
|
||||||
|
readOnly: true
|
||||||
|
})
|
||||||
|
const change = (editor) => {
|
||||||
|
emits('change', editor)
|
||||||
|
emits('update:modelValue', valueHtml.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const editorRef = shallowRef()
|
||||||
|
const valueHtml = ref('')
|
||||||
|
|
||||||
|
// 组件销毁时,也及时销毁编辑器
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
const editor = editorRef.value
|
||||||
|
if (editor == null) return
|
||||||
|
editor.destroy()
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleCreated = (editor) => {
|
||||||
|
editorRef.value = editor
|
||||||
|
valueHtml.value = props.modelValue
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.modelValue, () => {
|
||||||
|
valueHtml.value = props.modelValue
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-upload
|
||||||
|
multiple
|
||||||
|
:action="`${path}/fileUploadAndDownload/upload?noSave=1`"
|
||||||
|
:on-error="uploadError"
|
||||||
|
:on-success="uploadSuccess"
|
||||||
|
:show-file-list="true"
|
||||||
|
:file-list="fileList"
|
||||||
|
:limit="limit"
|
||||||
|
:accept="accept"
|
||||||
|
class="upload-btn"
|
||||||
|
>
|
||||||
|
<el-button type="primary">上传文件</el-button>
|
||||||
|
</el-upload>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { useUserStore } from '@/pinia/modules/user'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'UploadCommon',
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
limit: {
|
||||||
|
type: Number,
|
||||||
|
default: 3
|
||||||
|
},
|
||||||
|
accept: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const path = ref(import.meta.env.VITE_BASE_API)
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const fullscreenLoading = ref(false)
|
||||||
|
|
||||||
|
const fileList = ref(props.modelValue)
|
||||||
|
|
||||||
|
const emits = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
watch(fileList.value, (val) => {
|
||||||
|
console.log(val)
|
||||||
|
emits('update:modelValue', val)
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
value => {
|
||||||
|
fileList.value = value
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
const uploadSuccess = (res) => {
|
||||||
|
const { data } = res
|
||||||
|
if (data.file) {
|
||||||
|
fileList.value.push({
|
||||||
|
name: data.file.name,
|
||||||
|
url: data.file.url
|
||||||
|
})
|
||||||
|
fullscreenLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadError = () => {
|
||||||
|
ElMessage({
|
||||||
|
type: 'error',
|
||||||
|
message: '上传失败'
|
||||||
|
})
|
||||||
|
fullscreenLoading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,485 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
v-if="!multiple"
|
||||||
|
class="update-image"
|
||||||
|
:style="{
|
||||||
|
'background-image': `url(${getUrl(model)})`,
|
||||||
|
'position': 'relative',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-icon
|
||||||
|
v-if="isVideoExt(model || '')"
|
||||||
|
:size="32"
|
||||||
|
class="video video-icon"
|
||||||
|
style=""
|
||||||
|
>
|
||||||
|
<VideoPlay />
|
||||||
|
</el-icon>
|
||||||
|
<video
|
||||||
|
v-if="isVideoExt(model || '')"
|
||||||
|
class="avatar video-avatar video"
|
||||||
|
muted
|
||||||
|
preload="metadata"
|
||||||
|
style=""
|
||||||
|
@click="openChooseImg"
|
||||||
|
>
|
||||||
|
<source :src="getUrl(model) + '#t=1'">
|
||||||
|
</video>
|
||||||
|
<span
|
||||||
|
v-if="model"
|
||||||
|
class="update"
|
||||||
|
style="position: absolute;"
|
||||||
|
@click="openChooseImg"
|
||||||
|
>
|
||||||
|
<el-icon>
|
||||||
|
<delete />
|
||||||
|
</el-icon>
|
||||||
|
删除</span>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
class="update text-gray-600"
|
||||||
|
@click="openChooseImg"
|
||||||
|
>
|
||||||
|
<el-icon>
|
||||||
|
<plus />
|
||||||
|
</el-icon>
|
||||||
|
上传</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="multiple-img"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in multipleValue"
|
||||||
|
:key="index"
|
||||||
|
class="update-image"
|
||||||
|
:style="{
|
||||||
|
'background-image': `url(${getUrl(item)})`,
|
||||||
|
'position': 'relative',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-icon
|
||||||
|
v-if="isVideoExt(item || '')"
|
||||||
|
:size="32"
|
||||||
|
class="video video-icon"
|
||||||
|
>
|
||||||
|
<VideoPlay />
|
||||||
|
</el-icon>
|
||||||
|
<video
|
||||||
|
v-if="isVideoExt(item || '')"
|
||||||
|
class="avatar video-avatar video"
|
||||||
|
muted
|
||||||
|
preload="metadata"
|
||||||
|
@click="deleteImg(index)"
|
||||||
|
>
|
||||||
|
<source :src="getUrl(item) + '#t=1'">
|
||||||
|
</video>
|
||||||
|
<span
|
||||||
|
class="update"
|
||||||
|
style="position: absolute;"
|
||||||
|
@click="deleteImg(index)"
|
||||||
|
>
|
||||||
|
<el-icon>
|
||||||
|
<delete />
|
||||||
|
</el-icon>
|
||||||
|
删除</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="!maxUpdateCount || maxUpdateCount>multipleValue.length"
|
||||||
|
class="add-image"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="update text-gray-600"
|
||||||
|
@click="openChooseImg"
|
||||||
|
>
|
||||||
|
<el-icon>
|
||||||
|
<Plus />
|
||||||
|
</el-icon>
|
||||||
|
上传</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-drawer
|
||||||
|
v-model="drawer"
|
||||||
|
title="媒体库"
|
||||||
|
size="650px"
|
||||||
|
>
|
||||||
|
<warning-bar
|
||||||
|
title="点击“文件名/备注”可以编辑文件名或者备注内容。"
|
||||||
|
/>
|
||||||
|
<div class="gva-btn-list">
|
||||||
|
<upload-common
|
||||||
|
:image-common="imageCommon"
|
||||||
|
class="upload-btn-media-library"
|
||||||
|
@on-success="getImageList"
|
||||||
|
/>
|
||||||
|
<upload-image
|
||||||
|
:image-url="imageUrl"
|
||||||
|
:file-size="512"
|
||||||
|
:max-w-h="1080"
|
||||||
|
class="upload-btn-media-library"
|
||||||
|
@on-success="getImageList"
|
||||||
|
/>
|
||||||
|
<el-form
|
||||||
|
ref="searchForm"
|
||||||
|
:inline="true"
|
||||||
|
:model="search"
|
||||||
|
>
|
||||||
|
<el-form-item label="">
|
||||||
|
<el-input
|
||||||
|
v-model="search.keyword"
|
||||||
|
class="keyword"
|
||||||
|
placeholder="请输入文件名或备注"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="search"
|
||||||
|
@click="getImageList"
|
||||||
|
>查询
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<div class="media">
|
||||||
|
<div
|
||||||
|
v-for="(item,key) in picList"
|
||||||
|
:key="key"
|
||||||
|
class="media-box"
|
||||||
|
>
|
||||||
|
<div class="header-img-box-list">
|
||||||
|
<el-image
|
||||||
|
:key="key"
|
||||||
|
:src="getUrl(item.url)"
|
||||||
|
fit="cover"
|
||||||
|
style="width: 100%;height: 100%;"
|
||||||
|
@click="chooseImg(item.url)"
|
||||||
|
>
|
||||||
|
<template #error>
|
||||||
|
<el-icon
|
||||||
|
v-if="isVideoExt(item.url || '')"
|
||||||
|
:size="32"
|
||||||
|
class="video video-icon"
|
||||||
|
>
|
||||||
|
<VideoPlay />
|
||||||
|
</el-icon>
|
||||||
|
<video
|
||||||
|
v-if="isVideoExt(item.url || '')"
|
||||||
|
class="avatar video-avatar video"
|
||||||
|
muted
|
||||||
|
preload="metadata"
|
||||||
|
@click="chooseImg(item.url)"
|
||||||
|
>
|
||||||
|
<source :src="getUrl(item.url) + '#t=1'">
|
||||||
|
您的浏览器不支持视频播放
|
||||||
|
</video>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="header-img-box-list"
|
||||||
|
>
|
||||||
|
<el-icon class="lost-image">
|
||||||
|
<icon-picture />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="img-title"
|
||||||
|
@click="editFileNameFunc(item)"
|
||||||
|
>{{ item.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-pagination
|
||||||
|
:current-page="page"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:total="total"
|
||||||
|
:style="{'justify-content':'center'}"
|
||||||
|
layout="total, prev, pager, next, jumper"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
/>
|
||||||
|
</el-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
import { getUrl, isVideoExt } from '@/utils/image'
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
import { getFileList, editFileName } from '@/api/fileUploadAndDownload'
|
||||||
|
import UploadImage from '@/components/upload/image.vue'
|
||||||
|
import UploadCommon from '@/components/upload/common.vue'
|
||||||
|
import WarningBar from '@/components/warningBar/warningBar.vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { Delete, Plus, Picture as IconPicture } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
const imageUrl = ref('')
|
||||||
|
const imageCommon = ref('')
|
||||||
|
|
||||||
|
const search = ref({})
|
||||||
|
const page = ref(1)
|
||||||
|
const total = ref(0)
|
||||||
|
const pageSize = ref(20)
|
||||||
|
|
||||||
|
const model = defineModel({ type: [String, Array] })
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
multiple: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
fileType: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
maxUpdateCount: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const multipleValue = ref([])
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.multiple) {
|
||||||
|
multipleValue.value = model.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const deleteImg = (index) => {
|
||||||
|
multipleValue.value.splice(index, 1)
|
||||||
|
model.value = multipleValue.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
const handleSizeChange = (val) => {
|
||||||
|
pageSize.value = val
|
||||||
|
getImageList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCurrentChange = (val) => {
|
||||||
|
page.value = val
|
||||||
|
getImageList()
|
||||||
|
}
|
||||||
|
const editFileNameFunc = async(row) => {
|
||||||
|
ElMessageBox.prompt('请输入文件名或者备注', '编辑', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
inputPattern: /\S/,
|
||||||
|
inputErrorMessage: '不能为空',
|
||||||
|
inputValue: row.name
|
||||||
|
}).then(async({ value }) => {
|
||||||
|
row.name = value
|
||||||
|
// console.log(row)
|
||||||
|
const res = await editFileName(row)
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '编辑成功!',
|
||||||
|
})
|
||||||
|
getImageList()
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
ElMessage({
|
||||||
|
type: 'info',
|
||||||
|
message: '取消修改'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const drawer = ref(false)
|
||||||
|
const picList = ref([])
|
||||||
|
|
||||||
|
const imageTypeList = ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp']
|
||||||
|
const videoTyteList = ['mp4', 'avi', 'rmvb', 'rm', 'asf', 'divx', 'mpg', 'mpeg', 'mpe', 'wmv', 'mkv', 'vob']
|
||||||
|
|
||||||
|
const listObj = {
|
||||||
|
image: imageTypeList,
|
||||||
|
video: videoTyteList
|
||||||
|
}
|
||||||
|
|
||||||
|
const chooseImg = (url) => {
|
||||||
|
console.log(url)
|
||||||
|
if (props.fileType) {
|
||||||
|
const typeSuccess = listObj[props.fileType].some(item => {
|
||||||
|
if (url.includes(item)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!typeSuccess) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'error',
|
||||||
|
message: '当前类型不支持使用'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (props.multiple) {
|
||||||
|
multipleValue.value.push(url)
|
||||||
|
model.value = multipleValue.value
|
||||||
|
} else {
|
||||||
|
model.value = url
|
||||||
|
}
|
||||||
|
drawer.value = false
|
||||||
|
}
|
||||||
|
const openChooseImg = async() => {
|
||||||
|
if (model.value && !props.multiple) {
|
||||||
|
model.value = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await getImageList()
|
||||||
|
drawer.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const getImageList = async() => {
|
||||||
|
const res = await getFileList({ page: page.value, pageSize: pageSize.value, ...search.value })
|
||||||
|
if (res.code === 0) {
|
||||||
|
picList.value = res.data.list
|
||||||
|
total.value = res.data.total
|
||||||
|
page.value = res.data.page
|
||||||
|
pageSize.value = res.data.pageSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
.multiple-img {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-image {
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
line-height: 120px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 1px dashed #ccc;
|
||||||
|
background-size: cover;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-image {
|
||||||
|
cursor: pointer;
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
line-height: 120px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 1px dashed #ccc;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #fff;
|
||||||
|
background: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
rgba(255, 255, 255, 0.15) 0%,
|
||||||
|
rgba(0, 0, 0, 0.15) 100%
|
||||||
|
),
|
||||||
|
radial-gradient(
|
||||||
|
at top center,
|
||||||
|
rgba(255, 255, 255, 0.4) 0%,
|
||||||
|
rgba(0, 0, 0, 0.4) 120%
|
||||||
|
) #989898;
|
||||||
|
background-blend-mode: multiply, multiply;
|
||||||
|
background-size: cover;
|
||||||
|
|
||||||
|
.update {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video {
|
||||||
|
opacity: 0.2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-icon {
|
||||||
|
position: absolute;
|
||||||
|
left: calc(50% - 16px);
|
||||||
|
top: calc(50% - 16px);
|
||||||
|
}
|
||||||
|
|
||||||
|
video {
|
||||||
|
object-fit: cover;
|
||||||
|
max-width: 100%;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update {
|
||||||
|
height: 120px;
|
||||||
|
width: 120px;
|
||||||
|
text-align: center;
|
||||||
|
color: transparent;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-btn-media-library {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.media-box {
|
||||||
|
width: 120px;
|
||||||
|
margin-left: 20px;
|
||||||
|
|
||||||
|
.img-title {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
line-height: 36px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-img-box-list {
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
border: 1px dashed #ccc;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 120px;
|
||||||
|
cursor: pointer;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.el-image__inner {
|
||||||
|
max-width: 120px;
|
||||||
|
max-height: 120px;
|
||||||
|
vertical-align: middle;
|
||||||
|
width: unset;
|
||||||
|
height: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-image {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-icon {
|
||||||
|
position: absolute;
|
||||||
|
left: calc(50% - 16px);
|
||||||
|
top: calc(50% - 16px);
|
||||||
|
}
|
||||||
|
|
||||||
|
video {
|
||||||
|
object-fit: cover;
|
||||||
|
max-width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
:class="svgClass"
|
||||||
|
v-bind="$attrs"
|
||||||
|
:color="color"
|
||||||
|
>
|
||||||
|
<use
|
||||||
|
:xlink:href="'#'+name"
|
||||||
|
rel="external nofollow"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
const props = defineProps({
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: 'currentColor'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const svgClass = computed(() => {
|
||||||
|
if (props.name) {
|
||||||
|
return `svg-icon ${props.name}`
|
||||||
|
}
|
||||||
|
return 'svg-icon'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.svg-icon {
|
||||||
|
@apply w-4 h-4;
|
||||||
|
fill: currentColor;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-upload
|
||||||
|
:action="`${path}/fileUploadAndDownload/upload`"
|
||||||
|
:before-upload="checkFile"
|
||||||
|
:on-error="uploadError"
|
||||||
|
:on-success="uploadSuccess"
|
||||||
|
:show-file-list="false"
|
||||||
|
class="upload-btn"
|
||||||
|
>
|
||||||
|
<el-button type="primary">普通上传</el-button>
|
||||||
|
</el-upload>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { isVideoMime, isImageMime } from '@/utils/image'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'UploadCommon',
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['on-success'])
|
||||||
|
const path = ref(import.meta.env.VITE_BASE_API)
|
||||||
|
|
||||||
|
const fullscreenLoading = ref(false)
|
||||||
|
|
||||||
|
const checkFile = (file) => {
|
||||||
|
fullscreenLoading.value = true
|
||||||
|
const isLt500K = file.size / 1024 / 1024 < 0.5 // 500K, @todo 应支持在项目中设置
|
||||||
|
const isLt5M = file.size / 1024 / 1024 < 5 // 5MB, @todo 应支持项目中设置
|
||||||
|
const isVideo = isVideoMime(file.type)
|
||||||
|
const isImage = isImageMime(file.type)
|
||||||
|
let pass = true
|
||||||
|
if (!isVideo && !isImage) {
|
||||||
|
ElMessage.error('上传图片只能是 jpg,png,svg,webp 格式, 上传视频只能是 mp4,webm 格式!')
|
||||||
|
fullscreenLoading.value = false
|
||||||
|
pass = false
|
||||||
|
}
|
||||||
|
if (!isLt5M && isVideo) {
|
||||||
|
ElMessage.error('上传视频大小不能超过 5MB')
|
||||||
|
fullscreenLoading.value = false
|
||||||
|
pass = false
|
||||||
|
}
|
||||||
|
if (!isLt500K && isImage) {
|
||||||
|
ElMessage.error('未压缩的上传图片大小不能超过 500KB,请使用压缩上传')
|
||||||
|
fullscreenLoading.value = false
|
||||||
|
pass = false
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('upload file check result: ', pass)
|
||||||
|
|
||||||
|
return pass
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadSuccess = (res) => {
|
||||||
|
const { data } = res
|
||||||
|
if (data.file) {
|
||||||
|
emit('on-success', data.file.url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadError = () => {
|
||||||
|
ElMessage({
|
||||||
|
type: 'error',
|
||||||
|
message: '上传失败'
|
||||||
|
})
|
||||||
|
fullscreenLoading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-upload
|
||||||
|
:action="`${path}/fileUploadAndDownload/upload`"
|
||||||
|
:show-file-list="false"
|
||||||
|
:on-success="handleImageSuccess"
|
||||||
|
:before-upload="beforeImageUpload"
|
||||||
|
:multiple="false"
|
||||||
|
>
|
||||||
|
<el-button type="primary">压缩上传</el-button>
|
||||||
|
</el-upload>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import ImageCompress from '@/utils/image'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { useUserStore } from '@/pinia/modules/user'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'UploadImage',
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['on-success'])
|
||||||
|
const props = defineProps({
|
||||||
|
imageUrl: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
fileSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 2048 // 2M 超出后执行压缩
|
||||||
|
},
|
||||||
|
maxWH: {
|
||||||
|
type: Number,
|
||||||
|
default: 1920 // 图片长宽上限
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const path = ref(import.meta.env.VITE_BASE_API)
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
const beforeImageUpload = (file) => {
|
||||||
|
const isJPG = file.type === 'image/jpeg'
|
||||||
|
const isPng = file.type === 'image/png'
|
||||||
|
if (!isJPG && !isPng) {
|
||||||
|
ElMessage.error('上传头像图片只能是 jpg或png 格式!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const isRightSize = file.size / 1024 < props.fileSize
|
||||||
|
if (!isRightSize) {
|
||||||
|
// 压缩
|
||||||
|
const compress = new ImageCompress(file, props.fileSize, props.maxWH)
|
||||||
|
return compress.compress()
|
||||||
|
}
|
||||||
|
return isRightSize
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleImageSuccess = (res) => {
|
||||||
|
const { data } = res
|
||||||
|
if (data.file) {
|
||||||
|
emit('on-success', data.file.url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.image-uploader {
|
||||||
|
border: 1px dashed #d9d9d9;
|
||||||
|
width: 180px;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.image-uploader {
|
||||||
|
border-color: #409eff;
|
||||||
|
}
|
||||||
|
.image-uploader-icon {
|
||||||
|
font-size: 28px;
|
||||||
|
color: #8c939d;
|
||||||
|
width: 178px;
|
||||||
|
height: 178px;
|
||||||
|
line-height: 178px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.image {
|
||||||
|
width: 178px;
|
||||||
|
height: 178px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="px-1.5 py-2 flex items-center bg-amber-50 rounded gap-2 mb-3 text-amber-500"
|
||||||
|
:class="href&&'cursor-pointer'"
|
||||||
|
@click="open"
|
||||||
|
>
|
||||||
|
<el-icon class="text-xl">
|
||||||
|
<warning-filled />
|
||||||
|
</el-icon>
|
||||||
|
<span>
|
||||||
|
{{ title }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { WarningFilled } from '@element-plus/icons-vue'
|
||||||
|
const prop = defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
href: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const open = () => {
|
||||||
|
if (prop.href) {
|
||||||
|
window.open(prop.href)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
/*
|
||||||
|
* web框架组
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
// 加载网站配置文件夹
|
||||||
|
import { register } from './global'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install: (app) => {
|
||||||
|
register(app)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
/**
|
||||||
|
* 网站配置文件
|
||||||
|
*/
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
appName: '利农天下',
|
||||||
|
appLogo: '',
|
||||||
|
showViteLogo: true,
|
||||||
|
logs: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const viteLogo = (env) => {
|
||||||
|
console.log('哈哈哈')
|
||||||
|
}
|
||||||
|
|
||||||
|
export default config
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
import config from './config'
|
||||||
|
import { h } from 'vue'
|
||||||
|
|
||||||
|
// 统一导入el-icon图标
|
||||||
|
import * as ElIconModules from '@element-plus/icons-vue'
|
||||||
|
import svgIcon from '@/components/svgIcon/svgIcon.vue'
|
||||||
|
// 导入转换图标名称的函数
|
||||||
|
|
||||||
|
const createIconComponent = (name) => ({
|
||||||
|
name: 'SvgIcon',
|
||||||
|
render() {
|
||||||
|
return h(svgIcon, {
|
||||||
|
name: name,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const registerIcons = async (app) => {
|
||||||
|
const iconModules = import.meta.glob('@/assets/icons/**/*.svg')
|
||||||
|
for (const path in iconModules) {
|
||||||
|
const iconName = path.split('/').pop().replace(/\.svg$/, '')
|
||||||
|
// 如果iconName带空格则不加入到图标库中并且提示名称不合法
|
||||||
|
if (iconName.indexOf(' ') !== -1) {
|
||||||
|
console.error(`icon ${iconName}.svg includes whitespace`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const iconComponent = createIconComponent(iconName)
|
||||||
|
config.logs.push({
|
||||||
|
'key': iconName,
|
||||||
|
'label': iconName,
|
||||||
|
})
|
||||||
|
app.component(iconName, iconComponent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const register = (app) => {
|
||||||
|
// 统一注册el-icon图标
|
||||||
|
for (const iconName in ElIconModules) {
|
||||||
|
app.component(iconName, ElIconModules[iconName])
|
||||||
|
}
|
||||||
|
app.component('SvgIcon', svgIcon)
|
||||||
|
registerIcons(app)
|
||||||
|
app.config.globalProperties.$GIN_VUE_ADMIN = config
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
// 权限按钮展示指令
|
||||||
|
import { useUserStore } from '@/pinia/modules/user'
|
||||||
|
export default {
|
||||||
|
install: (app) => {
|
||||||
|
const userStore = useUserStore()
|
||||||
|
app.directive('auth', {
|
||||||
|
// 当被绑定的元素插入到 DOM 中时……
|
||||||
|
mounted: function(el, binding) {
|
||||||
|
const userInfo = userStore.userInfo
|
||||||
|
let type = ''
|
||||||
|
switch (Object.prototype.toString.call(binding.value)) {
|
||||||
|
case '[object Array]':
|
||||||
|
type = 'Array'
|
||||||
|
break
|
||||||
|
case '[object String]':
|
||||||
|
type = 'String'
|
||||||
|
break
|
||||||
|
case '[object Number]':
|
||||||
|
type = 'Number'
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
type = ''
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (type === '') {
|
||||||
|
el.parentNode.removeChild(el)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const waitUse = binding.value.toString().split(',')
|
||||||
|
let flag = waitUse.some(item => Number(item) === userInfo.authorityId)
|
||||||
|
if (binding.modifiers.not) {
|
||||||
|
flag = !flag
|
||||||
|
}
|
||||||
|
if (!flag) {
|
||||||
|
el.parentNode.removeChild(el)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
// 监听 window 的 resize 事件,返回当前窗口的宽高
|
||||||
|
import { shallowRef } from 'vue'
|
||||||
|
import { tryOnMounted, useEventListener } from '@vueuse/core'
|
||||||
|
|
||||||
|
const width = shallowRef(0)
|
||||||
|
const height = shallowRef(0)
|
||||||
|
|
||||||
|
export const useWindowResize = (cb) => {
|
||||||
|
const onResize = () => {
|
||||||
|
width.value = window.innerWidth
|
||||||
|
height.value = window.innerHeight
|
||||||
|
if (cb && typeof cb === 'function') {
|
||||||
|
cb(width.value, height.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tryOnMounted(onResize)
|
||||||
|
useEventListener('resize', onResize, { passive: true })
|
||||||
|
return {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { createPinia } from 'pinia'
|
||||||
|
|
||||||
|
const store = createPinia()
|
||||||
|
|
||||||
|
export {
|
||||||
|
store
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { findSysDictionary } from '@/api/sysDictionary'
|
||||||
|
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export const useDictionaryStore = defineStore('dictionary', () => {
|
||||||
|
const dictionaryMap = ref({})
|
||||||
|
|
||||||
|
const setDictionaryMap = (dictionaryRes) => {
|
||||||
|
dictionaryMap.value = { ...dictionaryMap.value, ...dictionaryRes }
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDictionary = async(type) => {
|
||||||
|
if (dictionaryMap.value[type] && dictionaryMap.value[type].length) {
|
||||||
|
return dictionaryMap.value[type]
|
||||||
|
} else {
|
||||||
|
const res = await findSysDictionary({ type })
|
||||||
|
if (res.code === 0) {
|
||||||
|
const dictionaryRes = {}
|
||||||
|
const dict = []
|
||||||
|
res.data.resysDictionary.sysDictionaryDetails && res.data.resysDictionary.sysDictionaryDetails.forEach(item => {
|
||||||
|
dict.push({
|
||||||
|
label: item.label,
|
||||||
|
value: item.value,
|
||||||
|
extend: item.extend
|
||||||
|
})
|
||||||
|
})
|
||||||
|
dictionaryRes[res.data.resysDictionary.type] = dict
|
||||||
|
setDictionaryMap(dictionaryRes)
|
||||||
|
return dictionaryMap.value[type]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
dictionaryMap,
|
||||||
|
setDictionaryMap,
|
||||||
|
getDictionary
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
import { asyncRouterHandle } from '@/utils/asyncRouter'
|
||||||
|
import { emitter } from '@/utils/bus.js'
|
||||||
|
import { asyncMenu } from '@/api/menu'
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
const notLayoutRouterArr = []
|
||||||
|
const keepAliveRoutersArr = []
|
||||||
|
const nameMap = {}
|
||||||
|
|
||||||
|
const formatRouter = (routes, routeMap, parent) => {
|
||||||
|
routes && routes.forEach(item => {
|
||||||
|
item.parent = parent
|
||||||
|
item.meta.btns = item.btns
|
||||||
|
item.meta.hidden = item.hidden
|
||||||
|
if (item.meta.defaultMenu === true) {
|
||||||
|
if (!parent) {
|
||||||
|
item = { ...item, path: `/${item.path}` }
|
||||||
|
notLayoutRouterArr.push(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
routeMap[item.name] = item
|
||||||
|
if (item.children && item.children.length > 0) {
|
||||||
|
formatRouter(item.children, routeMap, item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const KeepAliveFilter = (routes) => {
|
||||||
|
routes && routes.forEach(item => {
|
||||||
|
// 子菜单中有 keep-alive 的,父菜单也必须 keep-alive,否则无效。这里将子菜单中有 keep-alive 的父菜单也加入。
|
||||||
|
if ((item.children && item.children.some(ch => ch.meta.keepAlive) || item.meta.keepAlive)) {
|
||||||
|
item.component && item.component().then(val => {
|
||||||
|
keepAliveRoutersArr.push(val.default.name)
|
||||||
|
nameMap[item.name] = val.default.name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (item.children && item.children.length > 0) {
|
||||||
|
KeepAliveFilter(item.children)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useRouterStore = defineStore('router', () => {
|
||||||
|
const keepAliveRouters = ref([])
|
||||||
|
const asyncRouterFlag = ref(0)
|
||||||
|
const setKeepAliveRouters = (history) => {
|
||||||
|
const keepArrTemp = []
|
||||||
|
history.forEach(item => {
|
||||||
|
if (nameMap[item.name]) {
|
||||||
|
keepArrTemp.push(nameMap[item.name])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
keepAliveRouters.value = Array.from(new Set(keepArrTemp))
|
||||||
|
}
|
||||||
|
emitter.on('setKeepAlive', setKeepAliveRouters)
|
||||||
|
|
||||||
|
const asyncRouters = ref([])
|
||||||
|
const routeMap = ({})
|
||||||
|
// 从后台获取动态路由
|
||||||
|
const SetAsyncRouter = async() => {
|
||||||
|
asyncRouterFlag.value++
|
||||||
|
const baseRouter = [{
|
||||||
|
path: '/layout',
|
||||||
|
name: 'layout',
|
||||||
|
component: 'view/layout/index.vue',
|
||||||
|
meta: {
|
||||||
|
title: '底层layout'
|
||||||
|
},
|
||||||
|
children: []
|
||||||
|
}]
|
||||||
|
const asyncRouterRes = await asyncMenu()
|
||||||
|
const asyncRouter = asyncRouterRes.data.menus
|
||||||
|
asyncRouter && asyncRouter.push({
|
||||||
|
path: 'reload',
|
||||||
|
name: 'Reload',
|
||||||
|
hidden: true,
|
||||||
|
meta: {
|
||||||
|
title: '',
|
||||||
|
closeTab: true,
|
||||||
|
},
|
||||||
|
component: 'view/error/reload.vue'
|
||||||
|
})
|
||||||
|
formatRouter(asyncRouter, routeMap)
|
||||||
|
baseRouter[0].children = asyncRouter
|
||||||
|
if (notLayoutRouterArr.length !== 0) {
|
||||||
|
baseRouter.push(...notLayoutRouterArr)
|
||||||
|
}
|
||||||
|
asyncRouterHandle(baseRouter)
|
||||||
|
KeepAliveFilter(asyncRouter)
|
||||||
|
asyncRouters.value = baseRouter
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
asyncRouters,
|
||||||
|
keepAliveRouters,
|
||||||
|
asyncRouterFlag,
|
||||||
|
SetAsyncRouter,
|
||||||
|
routeMap
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
@ -0,0 +1,162 @@
|
||||||
|
import { login, getUserInfo, setSelfInfo } from '@/api/user'
|
||||||
|
import { jsonInBlacklist } from '@/api/jwt'
|
||||||
|
import router from '@/router/index'
|
||||||
|
import { ElLoading, ElMessage } from 'element-plus'
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { ref, computed, watch } from 'vue'
|
||||||
|
import { useRouterStore } from './router'
|
||||||
|
import cookie from 'js-cookie'
|
||||||
|
|
||||||
|
export const useUserStore = defineStore('user', () => {
|
||||||
|
const loadingInstance = ref(null)
|
||||||
|
|
||||||
|
const userInfo = ref({
|
||||||
|
uuid: '',
|
||||||
|
nickName: '',
|
||||||
|
headerImg: '',
|
||||||
|
authority: {},
|
||||||
|
sideMode: 'dark',
|
||||||
|
activeColor: 'var(--el-color-primary)',
|
||||||
|
baseColor: '#fff'
|
||||||
|
})
|
||||||
|
const token = ref(window.localStorage.getItem('token') || cookie.get('x-token') || '')
|
||||||
|
const setUserInfo = (val) => {
|
||||||
|
userInfo.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
const setToken = (val) => {
|
||||||
|
token.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
const NeedInit = () => {
|
||||||
|
token.value = ''
|
||||||
|
window.localStorage.removeItem('token')
|
||||||
|
router.push({ name: 'Init', replace: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResetUserInfo = (value = {}) => {
|
||||||
|
userInfo.value = {
|
||||||
|
...userInfo.value,
|
||||||
|
...value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* 获取用户信息*/
|
||||||
|
const GetUserInfo = async() => {
|
||||||
|
const res = await getUserInfo()
|
||||||
|
if (res.code === 0) {
|
||||||
|
setUserInfo(res.data.userInfo)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
/* 登录*/
|
||||||
|
const LoginIn = async(loginInfo) => {
|
||||||
|
loadingInstance.value = ElLoading.service({
|
||||||
|
fullscreen: true,
|
||||||
|
text: '登录中,请稍候...',
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
const res = await login(loginInfo)
|
||||||
|
if (res.code === 0) {
|
||||||
|
setUserInfo(res.data.user)
|
||||||
|
setToken(res.data.token)
|
||||||
|
const routerStore = useRouterStore()
|
||||||
|
await routerStore.SetAsyncRouter()
|
||||||
|
const asyncRouters = routerStore.asyncRouters
|
||||||
|
asyncRouters.forEach(asyncRouter => {
|
||||||
|
router.addRoute(asyncRouter)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!router.hasRoute(userInfo.value.authority.defaultRouter)) {
|
||||||
|
ElMessage.error('请联系管理员进行授权')
|
||||||
|
} else {
|
||||||
|
await router.replace({ name: userInfo.value.authority.defaultRouter })
|
||||||
|
}
|
||||||
|
|
||||||
|
loadingInstance.value.close()
|
||||||
|
|
||||||
|
const isWin = ref(/windows/i.test(navigator.userAgent))
|
||||||
|
if (isWin.value) {
|
||||||
|
window.localStorage.setItem('osType', 'WIN')
|
||||||
|
} else {
|
||||||
|
window.localStorage.setItem('osType', 'MAC')
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadingInstance.value.close()
|
||||||
|
}
|
||||||
|
loadingInstance.value.close()
|
||||||
|
}
|
||||||
|
/* 登出*/
|
||||||
|
const LoginOut = async() => {
|
||||||
|
const res = await jsonInBlacklist()
|
||||||
|
if (res.code === 0) {
|
||||||
|
await ClearStorage()
|
||||||
|
router.push({ name: 'Login', replace: true })
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* 清理数据 */
|
||||||
|
const ClearStorage = async() => {
|
||||||
|
token.value = ''
|
||||||
|
sessionStorage.clear()
|
||||||
|
window.localStorage.removeItem('token')
|
||||||
|
cookie.remove('x-token')
|
||||||
|
}
|
||||||
|
/* 设置侧边栏模式*/
|
||||||
|
const changeSideMode = async(data) => {
|
||||||
|
const res = await setSelfInfo({ sideMode: data })
|
||||||
|
if (res.code === 0) {
|
||||||
|
userInfo.value.sideMode = data
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '设置成功'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mode = computed(() => userInfo.value.sideMode)
|
||||||
|
const sideMode = computed(() => {
|
||||||
|
if (userInfo.value.sideMode === 'dark') {
|
||||||
|
return '#191a23'
|
||||||
|
} else if (userInfo.value.sideMode === 'light') {
|
||||||
|
return '#fff'
|
||||||
|
} else {
|
||||||
|
return userInfo.value.sideMode
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const baseColor = computed(() => {
|
||||||
|
if (userInfo.value.sideMode === 'dark') {
|
||||||
|
return '#fff'
|
||||||
|
} else if (userInfo.value.sideMode === 'light') {
|
||||||
|
return '#191a23'
|
||||||
|
} else {
|
||||||
|
return userInfo.value.baseColor
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const activeColor = computed(() => {
|
||||||
|
return 'var(--el-color-primary)'
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(() => token.value, () => {
|
||||||
|
window.localStorage.setItem('token', token.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
userInfo,
|
||||||
|
token,
|
||||||
|
NeedInit,
|
||||||
|
ResetUserInfo,
|
||||||
|
GetUserInfo,
|
||||||
|
LoginIn,
|
||||||
|
LoginOut,
|
||||||
|
changeSideMode,
|
||||||
|
mode,
|
||||||
|
sideMode,
|
||||||
|
setToken,
|
||||||
|
baseColor,
|
||||||
|
activeColor,
|
||||||
|
loadingInstance,
|
||||||
|
ClearStorage
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
import service from '@/utils/request'
|
||||||
|
// @Tags System
|
||||||
|
// @Summary 发送测试邮件
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Produce application/json
|
||||||
|
// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}"
|
||||||
|
// @Router /email/emailTest [post]
|
||||||
|
export const emailTest = (data) => {
|
||||||
|
return service({
|
||||||
|
url: '/email/emailTest',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags System
|
||||||
|
// @Summary 发送邮件
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body email_response.Email true "发送邮件必须的参数"
|
||||||
|
// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}"
|
||||||
|
// @Router /email/sendEmail [post]
|
||||||
|
export const sendEmail = (data) => {
|
||||||
|
return service({
|
||||||
|
url: '/email/sendEmail',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<warning-bar title="需要提前配置email配置文件,为防止不必要的垃圾邮件,在线体验功能不开放此功能体验。" />
|
||||||
|
<div class="gva-form-box">
|
||||||
|
<el-form
|
||||||
|
ref="emailForm"
|
||||||
|
label-position="right"
|
||||||
|
label-width="80px"
|
||||||
|
:model="form"
|
||||||
|
>
|
||||||
|
<el-form-item label="目标邮箱">
|
||||||
|
<el-input v-model="form.to" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="邮件">
|
||||||
|
<el-input v-model="form.subject" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="邮件内容">
|
||||||
|
<el-input
|
||||||
|
v-model="form.body"
|
||||||
|
type="textarea"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button @click="sendTestEmail">发送测试邮件</el-button>
|
||||||
|
<el-button @click="sendEmail">发送邮件</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import WarningBar from '@/components/warningBar/warningBar.vue'
|
||||||
|
import { emailTest } from '@/plugin/email/api/email.js'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { reactive, ref } from 'vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'Email',
|
||||||
|
})
|
||||||
|
|
||||||
|
const emailForm = ref(null)
|
||||||
|
const form = reactive({
|
||||||
|
to: '',
|
||||||
|
subject: '',
|
||||||
|
body: '',
|
||||||
|
})
|
||||||
|
const sendTestEmail = async() => {
|
||||||
|
const res = await emailTest()
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage.success('发送成功')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendEmail = async() => {
|
||||||
|
const res = await emailTest()
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage.success('发送成功,请查收')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||||
|
|
||||||
|
const routes = [{
|
||||||
|
path: '/',
|
||||||
|
redirect: '/login'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/init',
|
||||||
|
name: 'Init',
|
||||||
|
component: () => import('@/view/init/index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/login',
|
||||||
|
name: 'Login',
|
||||||
|
component: () => import('@/view/login/index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/:catchAll(.*)',
|
||||||
|
meta: {
|
||||||
|
closeTab: true,
|
||||||
|
},
|
||||||
|
component: () => import('@/view/error/index.vue')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHashHistory(),
|
||||||
|
routes,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
|
||||||
|
$colors: (
|
||||||
|
'white': #ffffff,
|
||||||
|
'black': #000000,
|
||||||
|
'primary': (
|
||||||
|
'base': #4d70ff,
|
||||||
|
),
|
||||||
|
'success': (
|
||||||
|
'base': #67c23a,
|
||||||
|
),
|
||||||
|
'warning': (
|
||||||
|
'base': #e6a23c,
|
||||||
|
),
|
||||||
|
'danger': (
|
||||||
|
'base': #f56c6c,
|
||||||
|
),
|
||||||
|
'error': (
|
||||||
|
'base': #f56c6c,
|
||||||
|
),
|
||||||
|
'info': (
|
||||||
|
'base': #909399,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
@import '@/style/main.scss';
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
font-weight: 400;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
@apply hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.gva-search-box {
|
||||||
|
@apply p-6 pb-0.5 bg-white rounded mb-3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gva-form-box {
|
||||||
|
@apply p-6 bg-white rounded;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gva-pagination {
|
||||||
|
@apply flex justify-end;
|
||||||
|
.el-pagination__editor {
|
||||||
|
.el-input__inner {
|
||||||
|
@apply h-8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-active {
|
||||||
|
@apply rounded text-white;
|
||||||
|
background: var(--el-color-primary);
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.el-drawer__header{
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
padding-top: 16px !important;
|
||||||
|
padding-bottom: 16px !important;
|
||||||
|
@apply border-0 border-b border-solid border-gray-200;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
@font-face {
|
||||||
|
font-family: 'gvaIcon';
|
||||||
|
src: url('data:font/ttf;charset=utf-8;base64,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AAAAAAALABMBkAADAAEECQAAACYAAAADAAEECQABABAAPAADAAEECQACAA4AVwADAAEECQADABAAbwADAAEECQAEABAAigADAAEECQAFABYApQADAAEECQAGABAAyQADAAEECQAKAFYA5AADAAEECQALACYBaABDAHIAZQBhAHQAZQBkACAAYgB5ACAAaQBjAG8AbgBmAG8AbgB0AABDcmVhdGVkIGJ5IGljb25mb250AABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABSAGUAZwB1AGwAYQByAABSZWd1bGFyAABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABWAGUAcgBzAGkAbwBuACAAMQAuADAAAFZlcnNpb24gMS4wAABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABHAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAHMAdgBnADIAdAB0AGYAIABmAHIAbwBtACAARgBvAG4AdABlAGwAbABvACAAcAByAG8AagBlAGMAdAAuAABHZW5lcmF0ZWQgYnkgc3ZnMnR0ZiBmcm9tIEZvbnRlbGxvIHByb2plY3QuAABoAHQAdABwADoALwAvAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAABodHRwOi8vZm9udGVsbG8uY29tAAAAAAIAAAAAAAAACgAAAAAAAQAAAAAAAAAAAAAAAAAAAAAACwAAAAEAAgECAQMBBAEFAQYBBwEIAQkRYXJyb3ctZG91YmxlLWxlZnQSYXJyb3ctZG91YmxlLXJpZ2h0EGN1c3RvbWVyLXNlcnZpY2URZnVsbHNjcmVlbi1leHBhbmQRZnVsbHNjcmVlbi1zaHJpbmsGcHJvbXB0B3JlZnJlc2gGc2VhcmNoAAAAAf//AAIAAQAAAAwAAAAWAAAAAgABAAMACgABAAQAAAACAAAAAAAAAAEAAAAA1aQnCAAAAADeWFEjAAAAAN5YUSM=') format('truetype');
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
.gvaIcon {
|
||||||
|
font-family: "gvaIcon" !important;
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 800;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gvaIcon-arrow-double-left:before {
|
||||||
|
content: "\e665";
|
||||||
|
}
|
||||||
|
|
||||||
|
.gvaIcon-arrow-double-right:before {
|
||||||
|
content: "\e666";
|
||||||
|
}
|
||||||
|
|
||||||
|
.gvaIcon-fullscreen-shrink:before {
|
||||||
|
content: "\e676";
|
||||||
|
}
|
||||||
|
.gvaIcon-customer-service:before {
|
||||||
|
content: "\e66a";
|
||||||
|
}
|
||||||
|
|
||||||
|
.gvaIcon-fullscreen-expand:before {
|
||||||
|
content: "\e675";
|
||||||
|
}
|
||||||
|
|
||||||
|
.gvaIcon-prompt:before {
|
||||||
|
content: "\e67b";
|
||||||
|
}
|
||||||
|
|
||||||
|
.gvaIcon-refresh:before {
|
||||||
|
content: "\e67c";
|
||||||
|
}
|
||||||
|
|
||||||
|
.gvaIcon-search:before {
|
||||||
|
content: "\e67d";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,701 @@
|
||||||
|
/* Document
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the line height in all browsers.
|
||||||
|
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@import '@/style/iconfont.css';
|
||||||
|
html {
|
||||||
|
line-height: 1.15;
|
||||||
|
/* 1 */
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Sections
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the margin in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the `main` element consistently in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
main {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the font size and margin on `h1` elements within `section` and
|
||||||
|
* `article` contexts in Chrome, Firefox, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Grouping content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in Firefox.
|
||||||
|
* 2. Show the overflow in Edge and IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
hr {
|
||||||
|
box-sizing: content-box;
|
||||||
|
/* 1 */
|
||||||
|
height: 0;
|
||||||
|
/* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-family: monospace, monospace;
|
||||||
|
/* 1 */
|
||||||
|
font-size: 1em;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Text-level semantics
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the gray background on active links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the bottom border in Chrome 57-
|
||||||
|
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
border-bottom: none;
|
||||||
|
/* 1 */
|
||||||
|
text-decoration: underline;
|
||||||
|
/* 2 */
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace;
|
||||||
|
/* 1 */
|
||||||
|
font-size: 1em;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font size in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||||
|
* all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Embedded content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the border on images inside links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Forms
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Change the font styles in all browsers.
|
||||||
|
* 2. Remove the margin in Firefox and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font-family: inherit;
|
||||||
|
/* 1 */
|
||||||
|
font-size: 100%;
|
||||||
|
/* 1 */
|
||||||
|
line-height: 1.15;
|
||||||
|
/* 1 */
|
||||||
|
margin: 0;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the overflow in IE.
|
||||||
|
* 1. Show the overflow in Edge.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input {
|
||||||
|
/* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||||
|
* 1. Remove the inheritance of text transform in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
/* 1 */
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
[type="button"],
|
||||||
|
[type="reset"],
|
||||||
|
[type="submit"] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner border and padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type="button"]::-moz-focus-inner,
|
||||||
|
[type="reset"]::-moz-focus-inner,
|
||||||
|
[type="submit"]::-moz-focus-inner {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the focus styles unset by the previous rule.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button:-moz-focusring,
|
||||||
|
[type="button"]:-moz-focusring,
|
||||||
|
[type="reset"]:-moz-focusring,
|
||||||
|
[type="submit"]:-moz-focusring {
|
||||||
|
outline: 1px dotted ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
padding: 0.35em 0.75em 0.625em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the text wrapping in Edge and IE.
|
||||||
|
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||||
|
* 3. Remove the padding so developers are not caught out when they zero out
|
||||||
|
* `fieldset` elements in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
legend {
|
||||||
|
box-sizing: border-box;
|
||||||
|
/* 1 */
|
||||||
|
color: inherit;
|
||||||
|
/* 2 */
|
||||||
|
display: table;
|
||||||
|
/* 1 */
|
||||||
|
max-width: 100%;
|
||||||
|
/* 1 */
|
||||||
|
padding: 0;
|
||||||
|
/* 3 */
|
||||||
|
white-space: normal;
|
||||||
|
/* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||||
|
*/
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the default vertical scrollbar in IE 10+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in IE 10.
|
||||||
|
* 2. Remove the padding in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="checkbox"],
|
||||||
|
[type="radio"] {
|
||||||
|
box-sizing: border-box;
|
||||||
|
/* 1 */
|
||||||
|
padding: 0;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="number"]::-webkit-inner-spin-button,
|
||||||
|
[type="number"]::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the odd appearance in Chrome and Safari.
|
||||||
|
* 2. Correct the outline style in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"] {
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
/* 1 */
|
||||||
|
outline-offset: -2px;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner padding in Chrome and Safari on macOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
* 2. Change font properties to `inherit` in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
/* 1 */
|
||||||
|
font: inherit;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Interactive
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
details {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Misc
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
HTML,
|
||||||
|
body,
|
||||||
|
div,
|
||||||
|
ul,
|
||||||
|
ol,
|
||||||
|
dl,
|
||||||
|
li,
|
||||||
|
dt,
|
||||||
|
dd,
|
||||||
|
p,
|
||||||
|
blockquote,
|
||||||
|
pre,
|
||||||
|
form,
|
||||||
|
fieldset,
|
||||||
|
table,
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
border: none;
|
||||||
|
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
address,
|
||||||
|
caption,
|
||||||
|
cite,
|
||||||
|
code,
|
||||||
|
th,
|
||||||
|
var {
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input::-ms-clear {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input::-ms-reveal {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
outline: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input::-webkit-input-placeholder {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
input::-ms-input-placeholder {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
input::-moz-placeholder {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=submit],
|
||||||
|
input[type=button] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button[disabled],
|
||||||
|
input[disabled] {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul,
|
||||||
|
ol,
|
||||||
|
li {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导航
|
||||||
|
#app {
|
||||||
|
.el-container {
|
||||||
|
@apply relative h-full w-full;
|
||||||
|
}
|
||||||
|
.el-container.mobile.openside {
|
||||||
|
@apply fixed top-0;
|
||||||
|
}
|
||||||
|
.gva-aside {
|
||||||
|
@apply fixed top-0 left-0 z-[1001] overflow-hidden;
|
||||||
|
.el-menu {
|
||||||
|
@apply border-r-0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.aside {
|
||||||
|
.el-menu--collapse {
|
||||||
|
>.el-menu-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-sub-menu {
|
||||||
|
.el-menu {
|
||||||
|
.is-active {
|
||||||
|
// 关闭三级菜单二级菜单样式
|
||||||
|
ul {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 关闭三级菜单二级菜单样式
|
||||||
|
.is-active.is-opened {
|
||||||
|
ul {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.hideside {
|
||||||
|
.aside {
|
||||||
|
@apply w-[54px]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile {
|
||||||
|
.gva-aside {
|
||||||
|
@apply w-[54px];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hideside {
|
||||||
|
.main-cont.el-main {
|
||||||
|
@apply ml-[54px];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.mobile {
|
||||||
|
.main-cont.el-main {
|
||||||
|
@apply ml-0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// layout
|
||||||
|
|
||||||
|
.admin-box {
|
||||||
|
@apply min-h-[calc(100vh-200px)] px-3 py-4 mt-28 mb-4 mx-1;
|
||||||
|
.el-table {
|
||||||
|
th {
|
||||||
|
@apply px-0 py-2;
|
||||||
|
.cell {
|
||||||
|
@apply leading-[40px] text-gray-700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
@apply px-0 py-2;
|
||||||
|
.cell {
|
||||||
|
@apply leading-[40px] text-gray-600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.is-leaf {
|
||||||
|
@apply border-b border-t-0 border-l-0 border-solid border-gray-50;
|
||||||
|
border-right:var(--el-table-border);
|
||||||
|
background: #F7FBFF !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// table
|
||||||
|
.el-pagination {
|
||||||
|
@apply mt-8;
|
||||||
|
.btn-prev,
|
||||||
|
.btn-next {
|
||||||
|
@apply border border-solid border-gray-300 rounded;
|
||||||
|
}
|
||||||
|
.el-pager {
|
||||||
|
li {
|
||||||
|
@apply border border-solid border-gray-300 rounded text-gray-600 text-sm mx-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.el-container.layout-cont {
|
||||||
|
.header-cont {
|
||||||
|
@apply px-4 h-16 bg-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.main-cont {
|
||||||
|
@apply h-screen overflow-visible;
|
||||||
|
&.el-main {
|
||||||
|
@apply min-h-full ml-[220px] bg-main p-0 overflow-auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb {
|
||||||
|
@apply h-16 flex items-center p-0 ml-12 text-lg;
|
||||||
|
.el-breadcrumb__item {
|
||||||
|
.el-breadcrumb__inner {
|
||||||
|
@apply text-gray-600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-breadcrumb__item:nth-last-child(1) {
|
||||||
|
.el-breadcrumb__inner {
|
||||||
|
@apply text-gray-600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.router-history {
|
||||||
|
@apply bg-white p-0 border-t border-l-0 border-r-0 border-b-0 border-solid border-gray-100;
|
||||||
|
.el-tabs__header {
|
||||||
|
@apply m-0;
|
||||||
|
.el-tabs__item{
|
||||||
|
@apply border-solid border-r border-t-0 border-gray-100 border-b-0 border-l-0;
|
||||||
|
}
|
||||||
|
.el-tabs__item.is-active {
|
||||||
|
@apply bg-blue-500 bg-opacity-5;
|
||||||
|
}
|
||||||
|
.el-tabs__nav {
|
||||||
|
@apply border-0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.aside {
|
||||||
|
@apply overflow-auto;
|
||||||
|
}
|
||||||
|
.el-menu-vertical {
|
||||||
|
@apply h-[calc(100vh-60px)];
|
||||||
|
&:not(.el-menu--collapse) {
|
||||||
|
@apply w-[220px];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-menu--collapse {
|
||||||
|
@apply w-[54px];
|
||||||
|
li {
|
||||||
|
.el-tooltip,
|
||||||
|
.el-sub-menu__title {
|
||||||
|
@apply px-4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dropdown {
|
||||||
|
@apply overflow-hidden
|
||||||
|
}
|
||||||
|
|
||||||
|
.gva-table-box {
|
||||||
|
@apply p-6 bg-white rounded;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gva-btn-list {
|
||||||
|
@apply mb-3 flex gap-3 items-center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#nprogress .bar {
|
||||||
|
background: #29d !important;
|
||||||
|
}
|
||||||
|
.gva-customer-icon{
|
||||||
|
@apply w-4 h-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form--inline {
|
||||||
|
.el-form-item {
|
||||||
|
& > .el-input, .el-cascader, .el-select, .el-date-editor, .el-autocomplete {
|
||||||
|
@apply w-52;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
const viewModules = import.meta.glob('../view/**/*.vue')
|
||||||
|
const pluginModules = import.meta.glob('../plugin/**/*.vue')
|
||||||
|
|
||||||
|
export const asyncRouterHandle = (asyncRouter) => {
|
||||||
|
asyncRouter.forEach(item => {
|
||||||
|
if (item.component && typeof item.component === 'string') {
|
||||||
|
if (item.component.split('/')[0] === 'view') {
|
||||||
|
item.component = dynamicImport(viewModules, item.component)
|
||||||
|
} else if (item.component.split('/')[0] === 'plugin') {
|
||||||
|
item.component = dynamicImport(pluginModules, item.component)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (item.children) {
|
||||||
|
asyncRouterHandle(item.children)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function dynamicImport(
|
||||||
|
dynamicViewsModules,
|
||||||
|
component
|
||||||
|
) {
|
||||||
|
const keys = Object.keys(dynamicViewsModules)
|
||||||
|
const matchKeys = keys.filter((key) => {
|
||||||
|
const k = key.replace('../', '')
|
||||||
|
return k === component
|
||||||
|
})
|
||||||
|
const matchKey = matchKeys[0]
|
||||||
|
|
||||||
|
return dynamicViewsModules[matchKey]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { reactive } from 'vue'
|
||||||
|
export const useBtnAuth = () => {
|
||||||
|
const route = useRoute()
|
||||||
|
return route.meta.btns || reactive({})
|
||||||
|
}
|
||||||
|
|
@ -24,12 +24,6 @@ export const formatOnlyDate = (time) => {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export const formatPriceType = (priceType) => {
|
|
||||||
switch (priceType) {
|
|
||||||
case 1: return '出厂价'
|
|
||||||
default: '未定义'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const filterDict = (value, options) => {
|
export const filterDict = (value, options) => {
|
||||||
const rowLabel = options && options.filter(item => item.value === value)
|
const rowLabel = options && options.filter(item => item.value === value)
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,14 @@
|
||||||
<div class="gva-top-card-left-title">早安,请开始一天的工作吧</div>
|
<div class="gva-top-card-left-title">早安,请开始一天的工作吧</div>
|
||||||
<div class="gva-top-card-left-dot">{{ weatherInfo }}</div>
|
<div class="gva-top-card-left-dot">{{ weatherInfo }}</div>
|
||||||
<div>
|
<div>
|
||||||
<!-- <div class="gva-top-card-left-item">
|
<div class="gva-top-card-left-item">
|
||||||
插件仓库:
|
插件仓库:
|
||||||
<a
|
<a
|
||||||
style="color:#409EFF"
|
style="color:#409EFF"
|
||||||
target="view_window"
|
target="view_window"
|
||||||
href="https://plugin.gin-vue-admin.com/#/layout/home"
|
href="https://plugin.gin-vue-admin.com/#/layout/home"
|
||||||
>https://plugin.gin-vue-admin.com</a>
|
>https://plugin.gin-vue-admin.com</a>
|
||||||
</div> -->
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<img
|
<img
|
||||||
|
|
|
||||||
|
|
@ -3,33 +3,29 @@ import axios from 'axios'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
const weatherInfo = ref('今日晴,0℃ - 10℃,天气寒冷,注意添加衣物。')
|
const weatherInfo = ref('今日晴,0℃ - 10℃,天气寒冷,注意添加衣物。')
|
||||||
const amapKey = '1f0c8b27920dc41d204800793d629d8e'
|
const amapKey = '8e8baa8a7317586c29ec694895de6e0a'
|
||||||
|
|
||||||
export const useWeatherInfo = () => {
|
export const useWeatherInfo = () => {
|
||||||
ip()
|
ip()
|
||||||
return weatherInfo
|
return weatherInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ip = async () => {
|
export const ip = async() => {
|
||||||
// key换成你自己的 https://console.amap.com/dev/index
|
// key换成你自己的 https://console.amap.com/dev/index
|
||||||
if (amapKey === '') {
|
if (amapKey === '') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const res = await axios.get('https://restapi.amap.com/v3/ip?key=' + amapKey)
|
const res = await axios.get('https://restapi.amap.com/v3/ip?key=' + amapKey)
|
||||||
var cityCode
|
if (res.data.adcode) {
|
||||||
if (res.data.status !== '0') {
|
getWeather(res.data.adcode)
|
||||||
cityCode = res.data.adcode
|
|
||||||
} else {
|
|
||||||
cityCode = '100000'
|
|
||||||
}
|
}
|
||||||
getWeather(cityCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getWeather = async (code) => {
|
const getWeather = async(code) => {
|
||||||
const url = 'https://restapi.amap.com/v3/weather/weatherInfo?key=' + amapKey + '&extensions=base&city=' + code
|
const response = await axios.get('https://restapi.amap.com/v3/weather/weatherInfo?key=' + amapKey + '&extensions=base&city=' + code)
|
||||||
const response = await axios.get(url)
|
|
||||||
if (response.data.status === '1') {
|
if (response.data.status === '1') {
|
||||||
const s = response.data.lives[0]
|
const s = response.data.lives[0]
|
||||||
weatherInfo.value = s.city + ' 天气:' + s.weather + ' 温度:' + s.temperature + '摄氏度 风向:' + s.winddirection + ' 风力:' + s.windpower + '级 空气湿度:' + s.humidity
|
weatherInfo.value = s.city + ' 天气:' + s.weather + ' 温度:' + s.temperature + '摄氏度 风向:' + s.winddirection + ' 风力:' + s.windpower + '级 空气湿度:' + s.humidity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue