This commit is contained in:
ljw
2024-09-13 16:34:15 +08:00
commit 364064e5ce
62 changed files with 8448 additions and 0 deletions
+99
View File
@@ -0,0 +1,99 @@
<template>
<el-form-item ref="formAddress" :label="label" :prop="prop">
<el-select v-model="currentProvince" clearable placeholder="省" @change="changeProvince">
<el-option v-for="(_, name) in pca" :key="name" :label="name" :value="name"/>
</el-select>
<el-select v-model="currentCity" clearable placeholder="市" @change="changeCity">
<el-option v-for="(_, name) in cities" :key="name" :label="name" :value="name"/>
</el-select>
<el-select v-model="currentCounty" clearable placeholder="区" @change="changeCounty">
<el-option v-for="item in counties" :key="item" :label="item" :value="item"/>
</el-select>
</el-form-item>
</template>
<script>
import { defineComponent, ref, computed } from 'vue'
import pca from '@/utils/pca.json'
export default defineComponent({
name: 'FormAddress',
props: {
prop: {
type: String,
default: '',
},
label: {
type: String,
default: '省/市/区',
},
province: {
type: String,
default: '',
},
city: {
type: String,
default: '',
},
county: {
type: String,
default: '',
},
},
setup (props, context) {
const cities = computed(() => pca[props.province] || [])
const counties = computed(() => pca[props.province] && pca[props.province][props.city] ? pca[props.province][props.city] : [])
let currentProvince = computed({
get: () => props.province,
set: (val) => {
context.emit('update:province', val)
},
})
let currentCity = computed({
get: () => props.city,
set: (val) => {
context.emit('update:city', val)
},
})
let currentCounty = computed({
get: () => props.county,
set: (val) => {
context.emit('update:county', val)
},
})
const changeProvince = (val) => {
currentCity = ''
currentCounty = ''
context.emit('changeProvince', val)
}
const changeCity = (val) => {
currentCounty = ''
context.emit('changeCity', val)
}
const changeCounty = (val) => {
context.emit('changeCounty', val)
}
return {
pca,
cities,
counties,
currentProvince,
currentCity,
currentCounty,
changeProvince,
changeCity,
changeCounty,
}
},
})
</script>
<style scoped>
</style>
+198
View File
@@ -0,0 +1,198 @@
<template>
<div class="upload-order-file">
<el-upload
size="mini"
ref="upload"
:on-success="fileUploadSuccess"
:before-upload="beforeFileUpload"
:on-preview="onPreview"
:on-remove="fileRemove"
:on-error="onError"
name="file"
:file-list="fileList"
:action="fileUploadHost"
:data="fileUploadData"
:headers="headers"
list-type="picture-card"
:limit="0"
accept="image/*"
>
<template #default>
<div class="default-slot">
<slot name="default">
<el-icon class="default-icon">
<plus/>
</el-icon>
</slot>
</div>
</template>
</el-upload>
<el-dialog v-model="showPreview" top="5vh">
<el-image :src="showImage" class="preview-image" fit="contain"></el-image>
</el-dialog>
</div>
</template>
<script>
import { defineComponent, ref, computed, reactive, unref, readonly, toRefs } from 'vue'
import { Plus, ZoomIn, Delete, ArrowLeft, ArrowRight, Check } from '@element-plus/icons'
import { useOss } from '@/components/form/upload/oss'
import { ElMessage } from 'element-plus'
import { useLocal } from '@/components/form/upload/local'
export default defineComponent({
name: 'imageUpload',
props: {
limit: {
type: Number,
default: 0,
},
beforeUpload: {
type: Function,
default: function () {
return true
},
},
host: {
type: String,
default: import.meta.env.VITE_BASE_API + '/file/upload',
},
modelValue: {
type: String,
default: '',
},
type: {
type: String,
default: 'local', //local oss
},
width: {
type: String,
default: '148px',
},
},
components: { Plus, ZoomIn, Delete, ArrowLeft, ArrowRight, Check },
setup (props, context) {
const showPreview = ref(false)
const showImage = ref('')
let fileList = computed(() => props.modelValue ? [{ url: props.modelValue, status: 'success' }] : [])
let fileUpload = reactive({
fileUploadHost: '',
fileUploadData: {},
beforeFileUpload: null,
headers: {},
})
if (props.type === 'oss') {
fileUpload = useOss(props.beforeUpload, props.multiple)
} else {
fileUpload = useLocal(props.beforeUpload, props.host)
}
function removeImage (file) {
let fList = unref(fileList)
const index = fList.findIndex(f => f.url === file.url)
fList.splice(index, 1)
updateValue(fList)
}
function updateValue (_fileList) {
let fList = unref(_fileList)
context.emit(
'update:modelValue',
fList.length ? fList[0].url : '',
)
}
function fileRemove (file, _fileList) {
updateValue(_fileList)
}
function onError () {
}
function fileUploadSuccess (response, file, _fileList) {
file.url = response?.data?.url || file.url
if (_fileList.length > 1) {
_fileList.splice(0, 1)
}
if (_fileList.every(f => f.status === 'success')) {
updateValue(_fileList)
}
}
function onPreview (file) {
showImage.value = file.url
showPreview.value = true
}
return {
fileList,
...toRefs(fileUpload),
fileRemove,
onError,
fileUploadSuccess,
onPreview,
removeImage,
showPreview,
showImage,
}
},
})
</script>
<style scoped lang="scss">
.upload-order-file {
::v-deep(.el-upload-list__item-thumbnail) {
object-fit: contain;
}
::v-deep(.el-upload--picture-card) {
width: v-bind(width);
height: v-bind(width);
}
::v-deep(.el-upload-list__item) {
width: v-bind(width);
height: v-bind(width);
}
::v-deep(.el-progress) {
width: v-bind(width) !important;
height: v-bind(width) !important;
}
::v-deep(.el-progress-circle) {
width: v-bind(width) !important;
height: v-bind(width) !important;
}
.default-slot {
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
.default-icon {
margin-top: 0;
}
}
}
.preview-image {
width: 100%;
::v-deep(img) {
max-height: 700px;
}
}
</style>
+272
View File
@@ -0,0 +1,272 @@
<template>
<div class="upload-order-file">
<el-upload
ref="upload"
:on-success="fileUploadSuccess"
:before-upload="beforeFileUpload"
:on-remove="fileRemove"
:on-exceed="onExceed"
:on-error="onError"
name="file"
:multiple="multiple"
:file-list="fileList"
:action="fileUploadHost"
:data="fileUploadData"
:headers="headers"
list-type="picture-card"
:limit="limit"
accept="image/*"
:drag="drag"
>
<template #default>
<div class="default-slot">
<slot name="default">
<div>
<el-icon class="default-icon">
<plus/>
</el-icon>
<div class="drag-tips">点击上传<span v-if="drag">或直接拖入文件</span></div>
</div>
</slot>
</div>
</template>
<template #file="{file}">
<img
v-if="file.status === 'success'"
class="el-upload-list__item-thumbnail"
:src="file.url"
alt=""
>
<label class="el-upload-list__item-status-label">
<el-icon color="white">
<check/>
</el-icon>
</label>
<el-progress
v-if="file.status === 'uploading'"
type="circle"
:stroke-width="6"
:percentage="parseInt(file.percentage)"
/>
<span v-else-if="file.status === 'success'" class="el-upload-list__item-actions">
<el-icon class="el-upload-list__item-icon" @click="leftImage(file)"><arrow-left/></el-icon>
<el-icon class="el-upload-list__item-icon" @click="removeImage(file)"><Delete/></el-icon>
<el-icon class="el-upload-list__item-icon" @click="rightImage(file)"><arrow-right/></el-icon>
</span>
</template>
</el-upload>
</div>
</template>
<script>
import { defineComponent, ref, computed, reactive, unref, readonly, toRefs } from 'vue'
import { Plus, ZoomIn, Delete, ArrowLeft, ArrowRight, Check } from '@element-plus/icons'
import { useOss } from '@/components/form/upload/oss'
import { ElMessage } from 'element-plus'
import { useLocal } from '@/components/form/upload/local'
export default defineComponent({
name: 'imagesUpload',
props: {
drag: {
type: Boolean,
default: false,
},
limit: {
type: Number,
default: 0,
},
beforeUpload: {
type: Function,
default: function () {
return true
},
},
host: {
type: String,
default: import.meta.env.VITE_BASE_API + '/file/upload',
},
modelValue: {
type: Array,
default: function () {
return []
},
},
type: {
type: String,
default: 'local', //local oss
},
multiple: {
type: Boolean,
default: false,
},
width: {
type: String,
default: '148px',
},
},
components: { Plus, ZoomIn, Delete, ArrowLeft, ArrowRight, Check },
setup (props, context) {
let fileList = computed(() => props.modelValue.map(url => { return { url, status: 'success' } }))
let fileUpload = reactive({
fileUploadHost: '',
fileUploadData: {},
beforeFileUpload: null,
headers: {},
})
if (props.type === 'oss') {
fileUpload = useOss(props.beforeUpload, props.multiple)
} else {
fileUpload = useLocal(props.beforeUpload, props.host)
}
function leftImage (file) {
let fList = unref(fileList)
const index = fList.findIndex(f => f.url === file.url)
if (index === 0 || index === -1) {
return
}
fList[index] = fList.splice(index - 1, 1, fList[index])[0]
updateValue(fList)
}
function rightImage (file) {
let fList = unref(fileList)
const index = fList.findIndex(f => f.url === file.url)
if (index === fList.length - 1 || index === -1) {
return
}
fList[index] = fList.splice(index + 1, 1, fList[index])[0]
updateValue(fList)
}
function removeImage (file) {
let fList = unref(fileList)
const index = fList.findIndex(f => f.url === file.url)
fList.splice(index, 1)
updateValue(fList)
}
function updateValue (_fileList) {
let fList = unref(_fileList)
context.emit(
'update:modelValue',
fList.filter(f => f.status === 'success').map(file => file.url),
)
}
function fileRemove (file, _fileList) {
updateValue(_fileList)
}
function onError () {
}
function fileUploadSuccess (response, file, _fileList) {
file.url = response?.data?.url || file.url
if (_fileList.every(f => f.status === 'success')) {
updateValue(_fileList)
}
}
function onExceed () {
ElMessage.error('超出数量限制')
}
return {
fileList,
...toRefs(fileUpload),
onExceed,
fileRemove,
onError,
fileUploadSuccess,
leftImage,
rightImage,
removeImage,
}
},
})
</script>
<style scoped lang="scss">
.upload-order-file {
::v-deep(.el-upload-dragger) {
border: none;
width: 100%;
height: 100%;
}
::v-deep(.el-upload--picture-card) {
width: v-bind(width);
height: v-bind(width);
}
::v-deep(.el-upload-list__item) {
width: v-bind(width);
height: v-bind(width);
}
::v-deep(.el-progress) {
width: v-bind(width) !important;
height: v-bind(width) !important;
}
::v-deep(.el-progress-circle) {
width: v-bind(width) !important;
height: v-bind(width) !important;
}
.drag-tips {
font-size: 12px;
color: #999;
}
::v-deep(.el-upload-list__item) {
transition: none !important;
}
::v-deep(.el-upload-list) {
transition: none !important;
}
.el-upload-list__item-thumbnail {
object-fit: contain;
}
.el-upload-list__item-actions {
display: flex;
justify-content: space-around;
align-items: center;
&:after {
display: none;
}
.el-upload-list__item-icon {
cursor: pointer;
font-size: 20px;
color: #fff;
}
}
.default-slot {
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
.default-icon {
margin-top: 0;
}
}
}
</style>
+21
View File
@@ -0,0 +1,21 @@
import { getToken } from '@/utils/auth'
export function useLocal (beforeUp, host) {
const fileUploadData = {}
const fileUploadHost = host
const headers = { 'api-token': getToken() }
const beforeFileUpload = async (file) => {
if (beforeUp) {
const br = await beforeUp(file)
if (!br) { return Promise.reject() }
}
return Promise.resolve()
}
return {
fileUploadData,
fileUploadHost,
beforeFileUpload,
headers,
}
}
+52
View File
@@ -0,0 +1,52 @@
import { ossToken } from '@/api/file'
import { random_filename } from '@/utils/file'
import { reactive, ref } from 'vue'
export function useOss (beforeUp, multiple) {
let fileUploadData = reactive({
policy: '',
OSSAccessKeyId: '',
success_action_status: '200', // 让服务端返回200,不然,默认会返回204
callback: '',
signature: '',
'x:dir': '',
})
const fileExpire = ref(0)
const fileUploadHost = ref('')
const beforeFileUpload = async (file) => {
if (beforeUp) {
const br = await beforeUp(file)
if (!br) { return Promise.reject() }
}
const now = Date.parse(new Date()) / 1000
if (fileExpire.value < now) {
const res = await ossToken()
const obj = JSON.parse(res.data)
fileExpire.value = parseInt(obj['expire'])
fileUploadData.policy = obj['policy']
fileUploadData.OSSAccessKeyId = obj['accessid']
fileUploadData.callback = obj['callback']
fileUploadData.signature = obj['signature']
fileUploadData['x:dir'] = obj['dir']
fileUploadHost.value = obj['host']
}
//多选文件时需要这个,不然每个文件上传的都是一样的data
if (multiple) {
await new Promise(resolve => {
setTimeout(() => { resolve() }, 50)
})
}
fileUploadData['x:origin_filename'] = file.name
fileUploadData.key = fileUploadData['x:dir'] + random_filename(file.name)
return Promise.resolve()
}
return {
fileUploadHost,
fileUploadData,
beforeFileUpload,
headers: {},
}
}