Merge branch 'main' into lyc-dev

This commit is contained in:
lyc 2024-07-18 11:11:04 +08:00
commit b9c9269452
8 changed files with 310 additions and 277 deletions

View File

@ -29,7 +29,7 @@
"jsencrypt": "^3.3.2", "jsencrypt": "^3.3.2",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1", "pinia-plugin-persistedstate": "^3.2.1",
"vue-cropper": "^1.1.3", "vue-cropper": "^1.0.3",
"vue-router": "^4.4.0" "vue-router": "^4.4.0"
}, },
"devDependencies": { "devDependencies": {

View File

@ -8,7 +8,7 @@
http-equiv="Content-Security-Policy" http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"
/> --> /> -->
<meta http-equiv="Content-Security-Policy" content="connect-src *; default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:" /> <meta http-equiv="Content-Security-Policy" content="connect-src *; default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:" />
</head> </head>

View File

@ -7,7 +7,7 @@ export function getUserProfile() {
method: 'get' method: 'get'
}) })
} }
// 修改用户个人信息 // 修改用户个人信息
export function updateUserProfile(data) { export function updateUserProfile(data) {
return request({ return request({
@ -16,7 +16,7 @@ export function getUserProfile() {
data: data data: data
}) })
} }
// 用户密码重置 // 用户密码重置
export function updateUserPwd(oldPassword, newPassword) { export function updateUserPwd(oldPassword, newPassword) {
const data = { const data = {
@ -29,11 +29,14 @@ export function getUserProfile() {
params: data params: data
}) })
} }
// 用户头像上传 // 用户头像上传
export function uploadAvatar(data) { export function uploadAvatar(data) {
return request({ return request({
url: '/system/user/profile/avatar', url: '/system/user/profile/avatar',
headers: {
'Content-Type': 'multipart/form-data'
},
method: 'post', method: 'post',
data: data data: data
}) })
@ -46,4 +49,4 @@ export function updateUserInfo(data) {
method: 'put', method: 'put',
data: data data: data
}) })
} }

View File

@ -8,3 +8,13 @@ export const isHaveLocalFile = async (fileNewName)=>{
}) })
}) })
} }
export const parseCataByNode = (node) => {
if (node.parentNode) {
let arr = parseCataByNode(node.parentNode)
arr.push(node.id)
return arr
} else {
return [node.id]
}
}

View File

@ -77,7 +77,7 @@ import FileListItem from '@/views/prepare/container/file-list-item.vue'
import { getSmarttalkPage, moveSmarttalk } from '@/api/file' import { getSmarttalkPage, moveSmarttalk } from '@/api/file'
import { toTimeText } from '@/utils/date' import { toTimeText } from '@/utils/date'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { isHaveLocalFile } from '@/utils/talkFile' import { isHaveLocalFile, parseCataByNode } from '@/utils/talkFile'
import FileOperBatch from '@/views/prepare/container/file-oper-batch.vue' import FileOperBatch from '@/views/prepare/container/file-oper-batch.vue'
const { ipcRenderer } = window.electron || {} const { ipcRenderer } = window.electron || {}
export default { export default {
@ -192,7 +192,7 @@ export default {
id: ids.join(), id: ids.join(),
textbookId: cataData.textBook.curBookId textbookId: cataData.textBook.curBookId
} }
let cata = this.parseCataByNode(cataData.node) let cata = parseCataByNode(cataData.node)
if ( if (
this.uploadData.levelFirstId == cata[0] && this.uploadData.levelFirstId == cata[0] &&
this.uploadData.levelSecondId == cata[1] && this.uploadData.levelSecondId == cata[1] &&
@ -250,7 +250,7 @@ export default {
nodeClick(data) { nodeClick(data) {
if (this.currentNode.id === data.node.id) return if (this.currentNode.id === data.node.id) return
this.checkFileList = [] this.checkFileList = []
let cata = this.parseCataByNode(data.node) let cata = parseCataByNode(data.node)
this.currentNode = data.node this.currentNode = data.node
this.uploadData.levelFirstId = cata[0] this.uploadData.levelFirstId = cata[0]
this.uploadData.levelSecondId = cata[1] this.uploadData.levelSecondId = cata[1]
@ -276,15 +276,6 @@ export default {
console.log(res) console.log(res)
this.isLoading = false this.isLoading = false
}) })
},
parseCataByNode(node) {
if (node.parentNode) {
let arr = this.parseCataByNode(node.parentNode)
arr.push(node.id)
return arr
} else {
return [node.id]
}
} }
} }
} }

View File

@ -31,10 +31,10 @@
</template> </template>
<script setup> <script setup>
import { ref, reactive } from 'vue' import { ref, reactive, getCurrentInstance } from 'vue'
import { updateUserPwd } from '@/api/system/user' import { updateUserPwd } from '@/api/system/user'
// const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance()
const user = reactive({ const user = reactive({
oldPassword: undefined, oldPassword: undefined,

View File

@ -1,175 +1,189 @@
<template> <template>
<div class="user-info-head" @click="editCropper()"> <div class="user-info-head" @click="editCropper()">
<img :src="options.img" title="点击上传头像" class="img-circle" /> <img :src="options.img" title="点击上传头像" class="img-circle img-lg" />
<el-dialog :title="title" v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog"> <el-dialog
<el-row> :title="title"
<el-col :xs="24" :md="12" :style="{ height: '350px' }"> v-model="open"
<vue-cropper width="800px"
ref="cropper" append-to-body
:img="options.img" @opened="modalOpened"
:info="true" @close="closeDialog"
:autoCrop="options.autoCrop" >
:autoCropWidth="options.autoCropWidth" <el-row>
:autoCropHeight="options.autoCropHeight" <el-col :xs="24" :md="12" :style="{ height: '350px' }">
:fixedBox="options.fixedBox" <vue-cropper
:outputType="options.outputType" ref="cropper"
@realTime="realTime" :img="options.img"
v-if="visible" :info="true"
/> :autoCrop="options.autoCrop"
</el-col> :autoCropWidth="options.autoCropWidth"
<el-col :xs="24" :md="12" :style="{ height: '350px' }"> :autoCropHeight="options.autoCropHeight"
<div class="avatar-upload-preview"> :fixedBox="options.fixedBox"
<img :src="options.previews.url" :style="options.previews.img" /> :outputType="options.outputType"
</div> @realTime="realTime"
</el-col> v-if="visible"
</el-row> />
<br /> </el-col>
<el-row> <el-col :xs="24" :md="12" :style="{ height: '350px' }">
<el-col :lg="2" :md="2"> <div class="avatar-upload-preview">
<el-upload <el-image style="width: 100px; height: 100px" :src="options.previews.url" :style="options.previews.img"/>
action="#" </div>
:http-request="requestUpload" </el-col>
:show-file-list="false" </el-row>
:before-upload="beforeUpload" <br />
> <el-row>
<el-button> <el-col :lg="2" :md="2">
选择 <el-upload
<el-icon class="el-icon--right"><Upload /></el-icon> action="#"
</el-button> :http-request="requestUpload"
</el-upload> :show-file-list="false"
</el-col> :before-upload="beforeUpload"
<el-col :lg="{ span: 1, offset: 2 }" :md="2"> >
<el-button :icon="Plus" @click="changeScale(1)"></el-button> <el-button>
</el-col> 选择
<el-col :lg="{ span: 1, offset: 1 }" :md="2"> <el-icon class="el-icon--right"><Upload /></el-icon>
<el-button :icon="Minus" @click="changeScale(-1)"></el-button> </el-button>
</el-col> </el-upload>
<el-col :lg="{ span: 1, offset: 1 }" :md="2"> </el-col>
<el-button :icon="RefreshLeft" @click="rotateLeft()"></el-button> <el-col :lg="{ span: 1, offset: 2 }" :md="2">
</el-col> <el-button :icon="Plus" @click="changeScale(1)"></el-button>
<el-col :lg="{ span: 1, offset: 1 }" :md="2"> </el-col>
<el-button :icon="RefreshRight" @click="rotateRight()"></el-button> <el-col :lg="{ span: 1, offset: 1 }" :md="2">
</el-col> <el-button :icon="Minus" @click="changeScale(-1)"></el-button>
<el-col :lg="{ span: 2, offset: 6 }" :md="2"> </el-col>
<el-button type="primary" @click="uploadImg()"> </el-button> <el-col :lg="{ span: 1, offset: 1 }" :md="2">
</el-col> <el-button :icon="RefreshLeft" @click="rotateLeft()"></el-button>
</el-row> </el-col>
</el-dialog> <el-col :lg="{ span: 1, offset: 1 }" :md="2">
</div> <el-button :icon="RefreshRight" @click="rotateRight()"></el-button>
</template> </el-col>
<el-col :lg="{ span: 2, offset: 6 }" :md="2">
<el-button type="primary" @click="uploadImg()"> </el-button>
</el-col>
</el-row>
</el-dialog>
</div>
</template>
<script setup> <script setup>
import { Upload, Plus, Minus, RefreshLeft, RefreshRight } from '@element-plus/icons-vue' import { Upload, Plus, Minus, RefreshLeft, RefreshRight } from '@element-plus/icons-vue'
import { ref, reactive } from 'vue' import { ref, reactive, getCurrentInstance } from 'vue'
import "vue-cropper/dist/index.css"; import 'vue-cropper/dist/index.css'
import { VueCropper } from "vue-cropper"; import { VueCropper } from 'vue-cropper'
import { uploadAvatar } from "@/api/system/user"; import { uploadAvatar } from '@/api/system/user'
import useUserStore from "@/store/modules/user"; import useUserStore from '@/store/modules/user'
import { ElMessage } from 'element-plus'
const userStore = useUserStore();
// const { proxy } = getCurrentInstance(); const userStore = useUserStore()
const { proxy } = getCurrentInstance()
const open = ref(false);
const visible = ref(false); const open = ref(false)
const title = ref("修改头像"); const visible = ref(false)
const title = ref('修改头像')
//
const options = reactive({ //
img: userStore.avatar, // const options = reactive({
autoCrop: true, // img: userStore.avatar, //
autoCropWidth: 200, // autoCrop: true, //
autoCropHeight: 200, // autoCropWidth: 200, //
fixedBox: true, // autoCropHeight: 200, //
outputType: "png", // PNG fixedBox: true, //
previews: {} // outputType: 'png', // PNG
}); previews: {} //
})
/** 编辑头像 */
function editCropper() { /** 编辑头像 */
open.value = true; function editCropper() {
} open.value = true
/** 打开弹出层结束时的回调 */ }
function modalOpened() { /** 打开弹出层结束时的回调 */
visible.value = true; function modalOpened() {
} visible.value = true
/** 覆盖默认上传行为 */ }
function requestUpload() {} /** 覆盖默认上传行为 */
/** 向左旋转 */ function requestUpload() {}
function rotateLeft() { /** 向左旋转 */
proxy.$refs.cropper.rotateLeft(); function rotateLeft() {
} proxy.$refs.cropper.rotateLeft()
/** 向右旋转 */ }
function rotateRight() { /** 向右旋转 */
proxy.$refs.cropper.rotateRight(); function rotateRight() {
} proxy.$refs.cropper.rotateRight()
/** 图片缩放 */ }
function changeScale(num) { /** 图片缩放 */
num = num || 1; function changeScale(num) {
proxy.$refs.cropper.changeScale(num); num = num || 1
} proxy.$refs.cropper.changeScale(num)
/** 上传预处理 */ }
function beforeUpload(file) { /** 上传预处理 */
if (file.type.indexOf("image/") == -1) { function beforeUpload(file) {
proxy.$modal.msgError("文件格式错误,请上传图片类型,如JPGPNG后缀的文件。"); if (file.type.indexOf('image/') == -1) {
} else { ElMessage({
const reader = new FileReader(); message: '文件格式错误,请上传图片类型,如JPGPNG后缀的文件。',
reader.readAsDataURL(file); type: 'error',
reader.onload = () => { })
options.img = reader.result; } else {
}; const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => {
options.img = reader.result
} }
} }
/** 上传图片 */ }
function uploadImg() { /** 上传图片 */
proxy.$refs.cropper.getCropBlob(data => { function uploadImg() {
let formData = new FormData(); proxy.$refs.cropper.getCropBlob((data) => {
formData.append("avatarfile", data); let formData = new FormData()
uploadAvatar(formData).then(response => { formData.append('avatarfile', data)
open.value = false; uploadAvatar(formData).then((response) => {
options.img = import.meta.env.VITE_APP_BASE_API + response.imgUrl; open.value = false
userStore.avatar = options.img; options.img = import.meta.env.VITE_APP_BASE_API + response.imgUrl
proxy.$modal.msgSuccess("修改成功"); userStore.avatar = options.img
visible.value = false; ElMessage({
}); message: '上传成功',
}); type: 'success',
} })
/** 实时预览 */ visible.value = false
function realTime(data) { })
options.previews = data; })
} }
/** 关闭窗口 */ /** 实时预览 */
function closeDialog() { function realTime(data) {
options.img = userStore.avatar; options.previews = data
options.visible = false; }
} /** 关闭窗口 */
</script> function closeDialog() {
options.img = userStore.avatar
options.visible = false
}
</script>
<style lang='scss' scoped> <style lang='scss' scoped>
.user-info-head { .user-info-head {
position: relative; position: relative;
display: inline-block; display: inline-block;
height: 120px; height: 120px;
} }
.user-info-head:hover:after { .user-info-head:hover:after {
content: "+"; content: '+';
position: absolute; position: absolute;
left: 0; left: 0;
right: 0; right: 0;
top: 0; top: 0;
bottom: 0; bottom: 0;
color: #eee; color: #eee;
background: rgba(0, 0, 0, 0.5); background: rgba(0, 0, 0, 0.5);
font-size: 24px; font-size: 24px;
font-style: normal; font-style: normal;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
cursor: pointer; cursor: pointer;
line-height: 110px; line-height: 110px;
border-radius: 50%; border-radius: 50%;
} }
.img-circle { .img-circle {
height: 120px; height: 120px;
width: auto width: auto;
} }
</style> </style>

View File

@ -1,89 +1,104 @@
<template> <template>
<el-form ref="userRef" :model="user" :rules="rules" label-width="80px"> <el-form ref="userRef" :model="user" :rules="rules" label-width="80px">
<el-form-item label="用户昵称" prop="nickName"> <el-form-item label="用户昵称" prop="nickName">
<el-input v-model="user.nickName" maxlength="30" /> <el-input v-model="user.nickName" maxlength="30" />
</el-form-item> </el-form-item>
<el-form-item label="手机号码" prop="phonenumber"> <el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="user.phonenumber" maxlength="11" /> <el-input v-model="user.phonenumber" maxlength="11" />
</el-form-item> </el-form-item>
<el-form-item label="邮箱" prop="email"> <el-form-item label="邮箱" prop="email">
<el-input v-model="user.email" maxlength="50" /> <el-input v-model="user.email" maxlength="50" />
</el-form-item> </el-form-item>
<el-form-item label="性别"> <el-form-item label="性别">
<el-radio-group > <el-radio-group v-model="user.sex">
<el-radio label="0"></el-radio> <el-radio value="0"></el-radio>
<el-radio label="1"></el-radio> <el-radio value="1"></el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="任教学科"> <el-form-item label="任教学科">
<el-radio-group v-model="user.edusubject"> <el-radio-group v-model="user.edusubject">
<template v-for="item in subjectList"> <template v-for="(item,index) in subjectList" :key="index">
<el-radio v-if="item.edustage==user.edustage" :label="item.itemtitle">{{ item.itemtitle }}</el-radio> <el-radio v-if="item.edustage == user.edustage" :value="item.itemtitle">
</template> {{item.itemtitle }}
</el-radio-group> </el-radio>
</el-form-item> </template>
<el-form-item> </el-radio-group>
<el-button type="primary" @click="submit">保存</el-button> </el-form-item>
<el-button type="primary" @click="handleclick1">测验</el-button> <el-form-item>
</el-form-item> <el-button type="primary" @click="submit">保存</el-button>
</el-form> </el-form-item>
</template> </el-form>
</template>
<script setup>
import { ref } from 'vue'
import { updateUserProfile } from "@/api/system/user";
import { listEvaluation } from "@/api/subject/index";
const props = defineProps({
user: {
type: Object
}
});
// const { proxy } = getCurrentInstance();
const rules = ref({
nickName: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
email: [{ required: true, message: "邮箱地址不能为空", trigger: "blur" }, { type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }],
phonenumber: [{ required: true, message: "手机号码不能为空", trigger: "blur" }, { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }],
});
const subjectList = ref([]);
function handleclick1() { <script setup>
console.log(props.user.edudegree) import { ref, getCurrentInstance } from 'vue'
} import { updateUserProfile } from '@/api/system/user'
import { listEvaluation } from '@/api/subject/index'
//
listEvaluation({ itemkey: "subject", pageSize: 500 }).then((res) => { const props = defineProps({
subjectList.value = res.rows; user: {
}); type: Object
}
setTimeout(() => { })
if (props.user.edudegree != '') { const { proxy } = getCurrentInstance()
var upcaseName = ['一年级', '二年级', '三年级', '四年级', '五年级', '六年级', '初一', '初二', '初三', '高一', '高二', '高三'];
var d = parseInt(props.user.edudegree.replace('年级', '')); const rules = ref({
if (d <= 6) { nickName: [{ required: true, message: '用户昵称不能为空', trigger: 'blur' }],
props.user.edustage = '小学'; email: [
} else if (d >= 7 && d <= 9) { { required: true, message: '邮箱地址不能为空', trigger: 'blur' },
props.user.edustage = '初中'; { type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] }
} else if (d >= 9 && d <= 12) { ],
props.user.edustage = '高中'; phonenumber: [
} { required: true, message: '手机号码不能为空', trigger: 'blur' },
props.user.stagelabel = upcaseName[d-1]; { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' }
} ]
}, 1000); })
/** 提交按钮 */ const subjectList = ref([])
function submit() {
proxy.$refs.userRef.validate(valid => { //
if (valid) { listEvaluation({ itemkey: 'subject', pageSize: 500 }).then((res) => {
console.log(props.user); subjectList.value = res.rows
updateUserProfile(props.user).then(response => { })
proxy.$modal.msgSuccess("修改成功");
}); setTimeout(() => {
} if (props.user.edudegree != '') {
}); var upcaseName = [
}; '一年级',
</script> '二年级',
'三年级',
'四年级',
'五年级',
'六年级',
'初一',
'初二',
'初三',
'高一',
'高二',
'高三'
]
var d = parseInt(props.user.edudegree.replace('年级', ''))
if (d <= 6) {
props.user.edustage = '小学'
} else if (d >= 7 && d <= 9) {
props.user.edustage = '初中'
} else if (d >= 9 && d <= 12) {
props.user.edustage = '高中'
}
props.user.stagelabel = upcaseName[d - 1]
}
}, 1000)
/** 提交按钮 */
function submit() {
proxy.$refs.userRef.validate((valid) => {
if (valid) {
console.log(props.user)
updateUserProfile(props.user).then((response) => {
proxy.$modal.msgSuccess('修改成功')
})
}
})
}
</script>