提交 69cbbaad authored 作者: lhh's avatar lhh

update

上级 f3335948
......@@ -7,7 +7,14 @@
"$ref": true,
"$shallowRef": true,
"$toRef": true,
"Component": true,
"ComponentPublicInstance": true,
"ComputedRef": true,
"EffectScope": true,
"InjectionKey": true,
"PropType": true,
"Ref": true,
"VNode": true,
"asyncComputed": true,
"autoResetRef": true,
"computed": true,
......@@ -85,7 +92,6 @@
"refThrottled": true,
"refWithControl": true,
"resolveComponent": true,
"resolveDirective": true,
"resolveRef": true,
"resolveUnref": true,
"shallowReactive": true,
......@@ -100,6 +106,7 @@
"toReactive": true,
"toRef": true,
"toRefs": true,
"toValue": true,
"triggerRef": true,
"tryOnBeforeMount": true,
"tryOnBeforeUnmount": true,
......@@ -287,14 +294,6 @@
"watchThrottled": true,
"watchTriggerable": true,
"watchWithFilter": true,
"whenever": true,
"Component": true,
"ComponentPublicInstance": true,
"ComputedRef": true,
"InjectionKey": true,
"PropType": true,
"Ref": true,
"VNode": true,
"toValue": true
"whenever": true
}
}
......@@ -90,7 +90,6 @@ declare global {
const refThrottled: typeof import('@vueuse/core')['refThrottled']
const refWithControl: typeof import('@vueuse/core')['refWithControl']
const resolveComponent: typeof import('vue')['resolveComponent']
const resolveDirective: typeof import('vue')['resolveDirective']
const resolveRef: typeof import('@vueuse/core')['resolveRef']
const resolveUnref: typeof import('@vueuse/core')['resolveUnref']
const shallowReactive: typeof import('vue')['shallowReactive']
......
......@@ -10,6 +10,7 @@
"dependencies": {
"@vueuse/core": "^9.4.0",
"axios": "^1.1.3",
"blueimp-md5": "^2.19.0",
"element-plus": "^2.2.19",
"file-saver": "^2.0.5",
"pinia": "^2.0.23",
......@@ -1040,6 +1041,11 @@
"node": ">=8"
}
},
"node_modules/blueimp-md5": {
"version": "2.19.0",
"resolved": "https://registry.npmmirror.com/blueimp-md5/-/blueimp-md5-2.19.0.tgz",
"integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w=="
},
"node_modules/boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
......@@ -5114,6 +5120,11 @@
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true
},
"blueimp-md5": {
"version": "2.19.0",
"resolved": "https://registry.npmmirror.com/blueimp-md5/-/blueimp-md5-2.19.0.tgz",
"integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w=="
},
"boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
......
......@@ -17,6 +17,7 @@
"dependencies": {
"@vueuse/core": "^9.4.0",
"axios": "^1.1.3",
"blueimp-md5": "^2.19.0",
"element-plus": "^2.2.19",
"file-saver": "^2.0.5",
"pinia": "^2.0.23",
......
// styles/element/index.scss
/* 只需要重写你需要的即可 */
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
$colors: (
'primary': (
'base': #aa1941
)
),
$dialog: (
'border-radius': '8px'
)
);
// 如果只是按需导入,则可以忽略以下内容。
// 如果你想导入所有样式:
@use 'element-plus/theme-chalk/src/index.scss' as *;
<script lang="ts" setup>
import { ElMessage } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
import type { UploadProps, UploadUserFile, UploadFile, UploadFiles, UploadInstance } from 'element-plus'
import md5 from 'blueimp-md5'
import axios from 'axios'
import { getSignature } from '@/api/base'
interface Props {
modelValue: string | { name: string; url: string }[]
prefix?: string
size?: number
beforeUpload?: UploadProps['beforeUpload']
onChange?: (uploadFile: UploadFile, uploadFiles: UploadFiles, uploadRef?: UploadInstance) => void
}
const props = withDefaults(defineProps<Props>(), {
prefix: 'upload/saas-learn/'
})
const emit = defineEmits(['update:modelValue', 'success'])
const uploadRef = ref<UploadInstance>()
const fileList = ref<UploadUserFile[]>([])
watch(
() => props.modelValue,
value => {
fileList.value = Array.isArray(value) ? value.map(item => ({ ...item })) : []
}
)
const showFileList = computed(() => {
return Array.isArray(props.modelValue)
})
// 自定义上传
const handleHttpRequest: UploadProps['httpRequest'] = async xhr => {
const name = xhr.file.name
const key = `${props.prefix}${md5(name + Date.now())}.${name.split('.').pop()}`
const signature: Record<string, any> = await getSignature()
const params = {
key,
host: signature.host,
OSSAccessKeyId: signature.accessid,
policy: signature.policy,
signature: signature.signature,
success_action_status: '200',
url: `${signature.host}/${key}`,
file: xhr.file
}
return axios
.post(params.host || 'https://webapp-pub.ezijing.com', Object.assign(params, xhr.data), {
headers: { 'Content-Type': 'multipart/form-data' },
onUploadProgress(progress: any) {
progress.percent = progress.total > 0 ? (progress.loaded / progress.total) * 100 : 0
xhr.onProgress(progress)
}
})
.then(() => {
return params
})
}
// 文件改变
const handleChange: UploadProps['onChange'] = (uploadFile, uploadFiles) => {
props.onChange && props.onChange(uploadFile, uploadFiles, uploadRef.value)
}
// 上传之前
const handleBeforeUpload: UploadProps['beforeUpload'] = file => {
if (props.size && file.size > props.size) {
ElMessage.error(`文件大小不能超过${props.size / 1024 / 1024}M`)
return false
}
if (props.beforeUpload) {
return props.beforeUpload(file)
}
}
// 上传限制
const handleExceed: UploadProps['onExceed'] = () => {
ElMessage.warning('文件超出个数限制')
}
// 上传成功
const handleSuccess: UploadProps['onSuccess'] = (response, uploadFile: any, uploadFiles) => {
if (!uploadFiles.every(item => item.status === 'success')) return
uploadFile.type = uploadFile.raw?.type
uploadFile.url = response.url
const value = showFileList.value
? uploadFiles.map((item: any) => {
return {
name: item.name,
url: item.url,
size: item.size || item.raw?.size,
type: item.type || item.raw?.type
}
})
: response.url
emit('update:modelValue', value)
emit('success', uploadFile, uploadFiles)
}
// 删除
const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
const value = showFileList.value ? uploadFiles : ''
emit('update:modelValue', value)
}
// 预览
const handlePreview: UploadProps['onPreview'] = uploadFile => {
window.open(uploadFile.url)
}
</script>
<template>
<el-upload
action="https://webapp-pub.ezijing.com"
:show-file-list="showFileList"
:http-request="handleHttpRequest"
:before-upload="handleBeforeUpload"
:on-change="handleChange"
:on-exceed="handleExceed"
:on-remove="handleRemove"
:on-preview="handlePreview"
:on-success="handleSuccess"
:file-list="fileList"
class="uploader"
ref="uploadRef">
<slot>
<template v-if="showFileList">
<template v-if="$attrs['list-type'] === 'picture-card'">
<el-icon><Plus /></el-icon>
</template>
<template v-else>
<el-button size="default" round>点击上传</el-button>
</template>
</template>
<div class="avatar-uploader" v-else>
<el-image :src="(modelValue as string)" fit="contain" v-if="modelValue" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</div>
</slot>
<template #tip>
<div class="el-upload__tip"><slot name="tip"></slot></div>
</template>
</el-upload>
</template>
<style lang="scss">
.uploader {
flex: 1;
overflow: hidden;
}
.avatar-uploader {
width: 178px;
height: 178px;
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
.el-image {
width: 100%;
height: 100%;
}
}
.avatar-uploader:hover {
border-color: var(--el-color-primary);
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 100%;
height: 100%;
text-align: center;
}
</style>
......@@ -47,10 +47,12 @@ function handleUser() {
window.open(LOGIN_URL)
}
}
const route = useRoute()
</script>
<template>
<header class="app-header" :class="classNames" ref="header">
<header class="app-header" :class="route.path === '/student' ? 'is-scrolled' : classNames" ref="header">
<div class="app-header-inner">
<div class="app-header-left">
<router-link to="/" class="logo"></router-link>
......@@ -58,6 +60,7 @@ function handleUser() {
<AppNav></AppNav>
<div class="app-header-right" v-if="!mobile">
<div class="study" @click="handleStudy" v-if="user.isLogin && user.courses.length > 0">立即学习</div>
<div class="study">学员展示</div>
<template v-if="user.isLogin">
<div @click="handleOrder" style="cursor: pointer">你好,{{ user.userName }}</div>
<div class="app-header-logout" @click="handleLogout">退出</div>
......@@ -133,7 +136,7 @@ function handleUser() {
.study {
font-size: 18px;
line-height: 50px;
margin-right: 30px;
margin-right: 20px;
font-weight: 400;
cursor: pointer;
}
......
......@@ -13,11 +13,7 @@ const navList = [
<template>
<nav class="app-nav">
<ul class="app-menu">
<TreeItem
:item="item"
v-for="(item, index) in navList"
:key="index"
></TreeItem>
<TreeItem :item="item" v-for="(item, index) in navList" :key="index"></TreeItem>
</ul>
</nav>
</template>
......
......@@ -6,6 +6,7 @@ import router from './router'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import '@/assets/css/element/index.scss'
import { Overlay, Tab, Tabs, ActionSheet } from 'vant'
// 2. 引入组件样式
import 'vant/lib/index.css'
......
import httpRequest from '@/utils/axios'
/**
* 创建订单
* */
export function createOrder(data?: any) {
return httpRequest.post('https://shop-show-pc.ezijing.com/api/shop/order/add', data)
}
/**
* 获取列表
*/
export function getAvatarList(params: any) {
return httpRequest.get('https://project-api.ezijing.com/api/psp/v1/welfare/avatar-list', { params })
}
/**
* 获取信息
*/
export function getAvatar() {
return httpRequest.get('https://project-api.ezijing.com/api/psp/v1/my/info')
}
/**
* 上传权益人头像
* */
export function uploadAvatar(data?: any) {
return httpRequest.post('https://project-api.ezijing.com/api/psp/v1/welfare/avatar', data)
}
\ No newline at end of file
<script setup lang="ts">
import AppUpload from '@/components/base/AppUpload.vue'
import { getAvatar, uploadAvatar } from '../api'
import { ElMessage } from 'element-plus'
let formData: any = $ref({ avatar: '' })
const rules = {
avatar: [{ required: true, message: '请上传头像', trigger: 'change' }]
}
const handleSubmit = function () {
if (formData.avatar !== '') {
uploadAvatar({ avatar: formData.avatar }).then(res => {
if (res.status) {
ElMessage.error(res.message)
} else {
ElMessage({ type: 'success', message: '提交成功' })
}
})
} else {
ElMessage({ message: '请上传头像' })
}
}
const getAvatarInfo = function () {
getAvatar().then(res => {
if (res.status === 400) {
ElMessage.error(res.message)
} else {
formData = res.data.info
}
})
}
onMounted(() => {
getAvatarInfo()
})
</script>
<template>
<div class="container">
<el-form :model="formData" :rules="rules" ref="form" label-width="80px">
<el-form-item label="姓名">
{{ formData.name }}
</el-form-item>
<el-form-item label="手机号">
{{ formData.mobile }}
</el-form-item>
<el-form-item label="证书编号">
{{ formData.certificate_number }}
</el-form-item>
<el-form-item prop="avatar" label="头像">
<AppUpload v-model="formData.avatar"></AppUpload>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSubmit">提交</el-button>
</el-form-item>
</el-form>
</div>
</template>
<style lang="scss">
.container {
max-width: 970px;
margin: 0 auto;
}
</style>
import type { RouteRecordRaw } from 'vue-router'
import AppLayout from '@/components/layout/Index.vue'
export const routes: Array<RouteRecordRaw> = [
{
path: '/student',
component: AppLayout,
props: { fixed: true },
children: [{ path: '', component: () => import('./views/Index.vue') }]
}
]
.section {
--section-title-color: #333;
--section-title-dot-bgcolor: #aa1941;
--section-title-dot-border-color: #c1ab85;
}
.section-title {
display: flex;
align-items: center;
justify-content: center;
padding: 40px 0;
h2 {
margin: 0 10px;
font-size: 32px;
font-weight: 500;
line-height: 1;
color: var(--section-title-color);
}
.section-title_dot {
position: relative;
width: 16px;
height: 16px;
margin: 0 14px;
border: 1px dotted var(--section-title-dot-border-color);
&::after {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
content: '';
width: 6px;
height: 6px;
background-color: var(--section-title-dot-bgcolor);
}
}
}
<script lang="ts" setup>
import { onMounted } from 'vue'
import { getAvatarList } from '../api'
import UploadAvatar from '../components/UploadAvatar.vue'
let list: any = $ref([])
let page = $ref(1)
let total = $ref(0)
const showIndex = $ref(1)
function infiniteHandler() {
getAvatarList({ page: page, page_size: 9 }).then(response => {
if (response.data.list) {
total = response.data.total
list = response.data.list
// $state.loaded()
} else {
// $state.complete()
}
})
}
const sizeChange = function (n) {
page = n
infiniteHandler()
}
onMounted(() => {
infiniteHandler()
})
</script>
<template>
<div class="student-box">
<div class="s-t">
<div class="t-btn">
<div @click="showIndex = 1" :class="showIndex === 1 ? 'btn active' : 'btn'">持证人展示</div>
<div @click="showIndex = 2" :class="showIndex === 2 ? 'btn active' : 'btn'">头像上传</div>
</div>
</div>
<div class="tap-item-box">
<div v-if="showIndex === 1">
<div class="avatar-list">
<template v-if="list.length">
<div class="avatar-item" v-for="item in list" :key="item.id">
<div class="content">
<p>{{ item.batch_name }}</p>
<h2>{{ item.name }}</h2>
<p>证书编号</p>
<p style="color: #cfb181">{{ item.certificate_number }}</p>
</div>
<img :src="item.avatar + '?x-oss-process=image/resize,m_fill,h_360,w_300'" class="pic" />
</div>
</template>
</div>
<div style="display: flex; justify-content: center">
<el-pagination class="page" @current-change="sizeChange" layout="prev, pager, next" :total="total" />
</div>
</div>
<UploadAvatar v-if="showIndex === 2"></UploadAvatar>
</div>
</div>
</template>
<style lang="scss">
.student-box {
.s-t {
position: relative;
width: 100%;
height: 190px;
background: url(https://zws-imgs-pub.ezijing.com/static/public/e5edeacd362ce7876f52c799f7e1a98d.png) center;
background-size: cover;
.t-btn {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 970px;
display: flex;
.btn {
width: 128px;
background: #aa1941;
margin-right: 2px;
font-size: 16px;
line-height: 36px;
text-align: center;
color: #fff;
cursor: pointer;
&.active {
color: #aa1941;
background: #fff;
}
}
}
}
.tap-item-box {
width: 970px;
margin: 40px auto;
.avatar-list {
display: flex;
flex-wrap: wrap;
width: 970px;
margin: 0 auto;
}
.avatar-item {
display: flex;
align-items: center;
flex: 0 0 298px;
width: 300px;
height: 180px;
margin-right: 20px;
margin-bottom: 20px;
background: url('@/assets/images/showcase_bg.png') no-repeat;
.content {
flex: 1;
margin-right: 10px;
overflow: hidden;
color: #fff;
padding: 10px;
h2 {
font-size: 20px;
font-weight: 500;
color: #cfb181;
margin-bottom: 10px;
}
p {
font-size: 14px;
color: #ffffff;
}
}
.pic {
width: 150px;
height: 100%;
object-fit: cover;
}
}
}
li.is-active {
color: #aa1941 !important;
}
}
</style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论