|
|
@@ -1,103 +1,235 @@
|
|
|
<template>
|
|
|
<el-container class="dashboard-container">
|
|
|
- <el-main class="movie-list">
|
|
|
- <div class="section-block">
|
|
|
- <h3 class="section-title">我的角色</h3>
|
|
|
- <div class="btn-group">
|
|
|
- <el-button
|
|
|
- v-for="item in roles"
|
|
|
- :key="item"
|
|
|
- icon="el-icon-user-solid"
|
|
|
- size="small"
|
|
|
- @click="goToRole(item)"
|
|
|
- >
|
|
|
- {{ item }}
|
|
|
- </el-button>
|
|
|
+ <el-main class="dashboard-main">
|
|
|
+
|
|
|
+ <div class="top-blocks-row">
|
|
|
+ <div class="section-block flex-item">
|
|
|
+ <h3 class="section-title">我的角色</h3>
|
|
|
+ <div class="btn-group">
|
|
|
+ <el-button
|
|
|
+ v-for="item in roles"
|
|
|
+ :key="item"
|
|
|
+ icon="el-icon-user-solid"
|
|
|
+ size="small"
|
|
|
+ class="custom-btn"
|
|
|
+ @click="goToRole(item)"
|
|
|
+ >
|
|
|
+ {{ item }}
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- <div class="section-block">
|
|
|
- <h3 class="section-title">Dashboard</h3>
|
|
|
- <div class="btn-group">
|
|
|
- <el-button icon="el-icon-files" size="small" @click="navTo('/disk')">Disk</el-button>
|
|
|
- <el-button icon="el-icon-film" size="small" @click="navTo('/')">VOD</el-button>
|
|
|
- <el-button icon="el-icon-document" size="small" @click="navTo('/blog')">Blog</el-button>
|
|
|
+ <div class="section-block flex-item">
|
|
|
+ <h3 class="section-title">快捷工作台</h3>
|
|
|
+ <div class="btn-group">
|
|
|
+ <el-button icon="el-icon-files" size="small" class="custom-btn" @click="navTo('/disk')">Disk</el-button>
|
|
|
+ <el-button icon="el-icon-film" size="small" class="custom-btn" @click="navTo('/')">VOD</el-button>
|
|
|
+ <el-button icon="el-icon-document" size="small" class="custom-btn" @click="navTo('/blog')">Blog</el-button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <div class="card-grid">
|
|
|
- <el-card class="box-card" shadow="hover">
|
|
|
- <div slot="header" class="clearfix">
|
|
|
- <span>我的资料</span>
|
|
|
+ <el-card class="box-card profile-card" shadow="hover">
|
|
|
+ <div slot="header" class="card-header-custom">
|
|
|
+ <i class="el-icon-postcard" />
|
|
|
+ <span>个人档案资料</span>
|
|
|
+ </div>
|
|
|
+ <div class="profile-card-content">
|
|
|
+ <div class="avatar-section">
|
|
|
+ <div class="avatar-wrapper">
|
|
|
+ <el-image :src="loginUser.avatarUrl" class="user-avatar" fit="cover">
|
|
|
+ <div slot="error" class="avatar-error-slot">
|
|
|
+ <i class="el-icon-user" />
|
|
|
+ </div>
|
|
|
+ </el-image>
|
|
|
+ </div>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ size="mini"
|
|
|
+ icon="el-icon-camera"
|
|
|
+ class="update-avatar-btn"
|
|
|
+ @click="openAvatarDialog"
|
|
|
+ >
|
|
|
+ 更换头像
|
|
|
+ </el-button>
|
|
|
</div>
|
|
|
- <el-form ref="profileForm" :model="loginUser" label-width="80px" size="small">
|
|
|
- <el-form-item label="性别">
|
|
|
- <el-input v-model="loginUser.gender" class="w-70" readonly />
|
|
|
- </el-form-item>
|
|
|
- <br />
|
|
|
- <el-form-item label="签名">
|
|
|
+
|
|
|
+ <el-form ref="profileForm" :model="loginUser" label-width="80px" size="small" class="profile-form">
|
|
|
+ <div class="form-grid">
|
|
|
+ <el-form-item label="用户 ID">
|
|
|
+ <el-input v-model="loginUser.userId" readonly class="is-readonly" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="用户名">
|
|
|
+ <el-input v-model="loginUser.username" readonly class="is-readonly" />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="显示名">
|
|
|
+ <div class="input-with-action">
|
|
|
+ <el-input v-model="loginUser.screenName" readonly />
|
|
|
+ <el-button type="text" icon="el-icon-edit" @click="openEditDialog('screenName', '显示名', loginUser.screenName)">修改</el-button>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="电子邮箱">
|
|
|
+ <div class="input-with-action">
|
|
|
+ <el-input v-model="loginUser.email" readonly />
|
|
|
+ <el-button type="text" icon="el-icon-edit" @click="openEditDialog('email', '电子邮箱', loginUser.email)">修改</el-button>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="手机号码">
|
|
|
+ <div class="input-with-action">
|
|
|
+ <el-input v-model="loginUser.mobile" readonly />
|
|
|
+ <el-button type="text" icon="el-icon-edit" @click="openEditDialog('mobile', '手机号码', loginUser.mobile)">修改</el-button>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="安全密码">
|
|
|
+ <div class="input-with-action">
|
|
|
+ <el-input value="******" type="password" readonly show-password />
|
|
|
+ <el-button type="text" icon="el-icon-key" @click="passwordDialogVisible = true">重置</el-button>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-form-item label="个性签名" class="full-width-item signature-item">
|
|
|
<el-input
|
|
|
+ ref="signatureInput"
|
|
|
v-model="loginUser.signature"
|
|
|
type="textarea"
|
|
|
:rows="3"
|
|
|
- class="w-70"
|
|
|
- readonly
|
|
|
+ :readonly="!isEditingSignature"
|
|
|
+ :placeholder="isEditingSignature ? '请输入您的个性签名...' : '这个人很懒,什么都没有留下~'"
|
|
|
+ resize="none"
|
|
|
/>
|
|
|
+ <div class="signature-actions">
|
|
|
+ <el-button v-if="!isEditingSignature" type="text" icon="el-icon-edit" @click="startEditSignature">修改签名</el-button>
|
|
|
+ <template v-else>
|
|
|
+ <el-button size="mini" type="primary" round @click="saveSignature">保存</el-button>
|
|
|
+ <el-button size="mini" round @click="cancelEditSignature">取消</el-button>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
- </el-card>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-main>
|
|
|
|
|
|
- <el-card class="box-card" shadow="hover">
|
|
|
- <div slot="header" class="clearfix">
|
|
|
- <span>我的帐号</span>
|
|
|
+ <el-dialog title="更新个人头像" :visible.sync="avatarDialogVisible" width="400px" append-to-body center custom-class="custom-dialog">
|
|
|
+ <div class="avatar-upload-box">
|
|
|
+ <el-upload
|
|
|
+ class="avatar-uploader"
|
|
|
+ :action="imgOssUrl"
|
|
|
+ :headers="imgHeaders"
|
|
|
+ :data="imgData"
|
|
|
+ :with-credentials="false"
|
|
|
+ :show-file-list="false"
|
|
|
+ :before-upload="beforeAvatarUpload"
|
|
|
+ :on-success="handleAvatarSuccess"
|
|
|
+ >
|
|
|
+ <div v-if="previewUrl" class="upload-preview-wrapper">
|
|
|
+ <img :src="previewUrl" class="upload-preview" alt="preview">
|
|
|
+ <div class="upload-mask"><i class="el-icon-edit" /><span>更换图片</span></div>
|
|
|
</div>
|
|
|
- <el-form ref="accountForm" :model="loginUser" label-width="80px" size="small">
|
|
|
- <el-form-item label="用户 ID">
|
|
|
- <el-input v-model="loginUser.userId" class="w-70" readonly />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="用户名">
|
|
|
- <el-input v-model="loginUser.username" class="w-70" readonly />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="显示名">
|
|
|
- <el-input v-model="loginUser.screenName" class="w-70" readonly />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="手机">
|
|
|
- <el-input v-model="loginUser.mobile" class="w-70" readonly />
|
|
|
- <el-button type="primary" plain size="mini" class="ml-10" @click="showUpdateDialog(1)">更新</el-button>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="密码">
|
|
|
- <el-input value="******" type="password" class="w-70" readonly show-password />
|
|
|
- <el-button type="primary" plain size="mini" class="ml-10" @click="showUpdateDialog(2)">更新</el-button>
|
|
|
- </el-form-item>
|
|
|
- </el-form>
|
|
|
- </el-card>
|
|
|
+ <div v-else-if="loginUser.avatarUrl" class="upload-preview-wrapper">
|
|
|
+ <img :src="loginUser.avatarUrl" class="upload-preview" alt="current">
|
|
|
+ <div class="upload-mask"><i class="el-icon-plus" /><span>上传新头像</span></div>
|
|
|
+ </div>
|
|
|
+ <div v-else class="upload-placeholder">
|
|
|
+ <i class="el-icon-plus" /><p>点击选择新头像</p>
|
|
|
+ </div>
|
|
|
+ </el-upload>
|
|
|
</div>
|
|
|
- </el-main>
|
|
|
+ <div class="upload-tip-text">仅支持 JPG 格式,大小不超过 2MB</div>
|
|
|
+ <span slot="footer" class="dialog-footer">
|
|
|
+ <el-button size="small" round @click="cancelAvatarUpdate">取 消</el-button>
|
|
|
+ <el-button size="small" type="primary" round :loading="submitLoading" @click="confirmAvatarUpdate">确 认</el-button>
|
|
|
+ </span>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <el-dialog :title="'修改' + editModel.title" :visible.sync="editDialogVisible" width="420px" append-to-body custom-class="custom-dialog">
|
|
|
+ <el-form label-position="top" size="small" style="padding: 0 10px;">
|
|
|
+ <el-form-item :label="'新的' + editModel.title">
|
|
|
+ <el-input v-model="editModel.value" autocomplete="off" clearable :placeholder="'请输入新的' + editModel.title" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <span slot="footer" class="dialog-footer">
|
|
|
+ <el-button size="small" round @click="editDialogVisible = false">取 消</el-button>
|
|
|
+ <el-button size="small" type="primary" round :loading="editSubmitLoading" @click="submitSingleField">确 认</el-button>
|
|
|
+ </span>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <el-dialog title="重置安全密码" :visible.sync="passwordDialogVisible" width="420px" append-to-body custom-class="custom-dialog">
|
|
|
+ <el-form ref="passwordForm" :model="passwordForm" label-position="top" size="small" style="padding: 0 10px;">
|
|
|
+ <el-form-item label="当前旧密码">
|
|
|
+ <el-input v-model="passwordForm.oldPassword" type="password" show-password placeholder="请输入当前密码验证身份" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="输入新密码">
|
|
|
+ <el-input v-model="passwordForm.newPassword" type="password" show-password placeholder="新密码长度建议大于6位" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <span slot="footer" class="dialog-footer">
|
|
|
+ <el-button size="small" round @click="closePasswordDialog">取 消</el-button>
|
|
|
+ <el-button size="small" type="primary" round :loading="passwordSubmitLoading" @click="submitPassword">确 认</el-button>
|
|
|
+ </span>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
</el-container>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
import { userMixin } from 'assets/js/mixin'
|
|
|
-import { getAuthedUser } from '@/utils/auth'
|
|
|
+import { getAuthedUser, updateAuthedUser } from '@/utils/auth'
|
|
|
+import { updateAvatar } from '@/api/account' // 假设你的其它更新 API 也在 account 或对应模块里
|
|
|
+import { getAvatarChannelInfo } from '@/api/file'
|
|
|
|
|
|
export default {
|
|
|
name: 'Dashboard',
|
|
|
mixins: [userMixin],
|
|
|
data() {
|
|
|
return {
|
|
|
- // 初始化防空指针报错
|
|
|
loginUser: {
|
|
|
+ avatarUrl: '',
|
|
|
gender: '',
|
|
|
signature: '',
|
|
|
userId: '',
|
|
|
username: '',
|
|
|
screenName: '',
|
|
|
mobile: '',
|
|
|
+ email: '',
|
|
|
roles: []
|
|
|
},
|
|
|
roles: [],
|
|
|
- machineStatList: [],
|
|
|
- sysInfo: null
|
|
|
+
|
|
|
+ // 头像上传状态
|
|
|
+ avatarDialogVisible: false,
|
|
|
+ submitLoading: false,
|
|
|
+ imgOssUrl: '',
|
|
|
+ imgHeaders: { Authorization: '' },
|
|
|
+ imgData: { channelCode: 0 },
|
|
|
+ previewUrl: '',
|
|
|
+ uploadSuccessData: null,
|
|
|
+
|
|
|
+ // 常规单字段修改弹窗状态
|
|
|
+ editDialogVisible: false,
|
|
|
+ editSubmitLoading: false,
|
|
|
+ editModel: {
|
|
|
+ field: '',
|
|
|
+ title: '',
|
|
|
+ value: ''
|
|
|
+ },
|
|
|
+
|
|
|
+ // 签名行内修改状态
|
|
|
+ isEditingSignature: false,
|
|
|
+ cachedSignature: '',
|
|
|
+
|
|
|
+ // 密码修改弹窗状态
|
|
|
+ passwordDialogVisible: false,
|
|
|
+ passwordSubmitLoading: false,
|
|
|
+ passwordForm: {
|
|
|
+ oldPassword: '',
|
|
|
+ newPassword: ''
|
|
|
+ }
|
|
|
}
|
|
|
},
|
|
|
created() {
|
|
|
@@ -111,18 +243,7 @@ export default {
|
|
|
this.loginUser = user
|
|
|
this.roles = user.roles || []
|
|
|
}
|
|
|
- this.getData()
|
|
|
- },
|
|
|
- getData() {
|
|
|
- // 异步获取其他数据
|
|
|
},
|
|
|
- goToRole(role) {
|
|
|
- this.$message.info(`role -> ${role}`)
|
|
|
- },
|
|
|
- /**
|
|
|
- * 统一路由跳转方法,代替原有的三个重复方法
|
|
|
- * @param {string} path 目标路径
|
|
|
- */
|
|
|
navTo(path) {
|
|
|
if (this.$route.path === path) {
|
|
|
this.$router.go(0)
|
|
|
@@ -130,58 +251,249 @@ export default {
|
|
|
}
|
|
|
this.$router.push(path)
|
|
|
},
|
|
|
- showUpdateDialog(type) {
|
|
|
- // 弹出更新弹窗的逻辑
|
|
|
- this.$message.success(`打开更新弹窗: ${type === 1 ? '手机' : '密码'}`)
|
|
|
- }
|
|
|
+
|
|
|
+ // ==================== 1. 常规单字段(显示名、邮箱、手机)修改业务 ====================
|
|
|
+ openEditDialog(field, title, currentVal) {
|
|
|
+ this.editModel.field = field
|
|
|
+ this.editModel.title = title
|
|
|
+ this.editModel.value = currentVal
|
|
|
+ this.editDialogVisible = true
|
|
|
+ },
|
|
|
+ submitSingleField() {
|
|
|
+ if (!this.editModel.value.trim()) {
|
|
|
+ return this.$message.warning(`请输入有效的${this.editModel.title}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ this.editSubmitLoading = true
|
|
|
+
|
|
|
+ // 模拟或者组装你的 API 请求参数,例如:
|
|
|
+ // const updateData = { [this.editModel.field]: this.editModel.value }
|
|
|
+ // updateUserInfo(updateData).then(res => { ... })
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ // 1. 动态同步到本地模型
|
|
|
+ this.loginUser[this.editModel.field] = this.editModel.value
|
|
|
+ // 2. 持久化到 localStorage/cookie 缓存
|
|
|
+ updateAuthedUser(this.loginUser)
|
|
|
+
|
|
|
+ this.$message.success(`${this.editModel.title}已成功修改`)
|
|
|
+ this.editDialogVisible = false
|
|
|
+ this.editSubmitLoading = false
|
|
|
+ }, 600)
|
|
|
+ },
|
|
|
+
|
|
|
+ // ==================== 2. 签名行内即时修改业务 ====================
|
|
|
+ startEditSignature() {
|
|
|
+ this.cachedSignature = this.loginUser.signature // 缓存旧签名以便取消
|
|
|
+ this.isEditingSignature = true
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$refs.signatureInput.focus() // 自动聚焦到输入框
|
|
|
+ })
|
|
|
+ },
|
|
|
+ saveSignature() {
|
|
|
+ // 触发后端 API 保存签名逻辑
|
|
|
+ this.$message.success('个性签名已同步更新')
|
|
|
+ updateAuthedUser(this.loginUser)
|
|
|
+ this.isEditingSignature = false
|
|
|
+ },
|
|
|
+ cancelEditSignature() {
|
|
|
+ this.loginUser.signature = this.cachedSignature // 恢复原状
|
|
|
+ this.isEditingSignature = false
|
|
|
+ },
|
|
|
+
|
|
|
+ // ==================== 3. 独立密码修改业务 ====================
|
|
|
+ submitPassword() {
|
|
|
+ if (!this.passwordForm.oldPassword || !this.passwordForm.newPassword) {
|
|
|
+ return this.$message.warning('请完整填写当前密码与新密码')
|
|
|
+ }
|
|
|
+ if (this.passwordForm.newPassword.length < 6) {
|
|
|
+ return this.$message.warning('为了您的账户安全,新密码不得少于6位')
|
|
|
+ }
|
|
|
+
|
|
|
+ this.passwordSubmitLoading = true
|
|
|
+ // 调用你的密码更新 API 接口
|
|
|
+ // changePassword(this.passwordForm).then(res => { ... })
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ this.$message.success('安全密码修改成功,请妥善保管')
|
|
|
+ this.closePasswordDialog()
|
|
|
+ }, 800)
|
|
|
+ },
|
|
|
+ closePasswordDialog() {
|
|
|
+ this.passwordDialogVisible = false
|
|
|
+ this.passwordForm.oldPassword = ''
|
|
|
+ this.passwordForm.newPassword = ''
|
|
|
+ this.passwordSubmitLoading = false
|
|
|
+ },
|
|
|
+
|
|
|
+ // ==================== 4. 头像模块云存储控制(保持不变) ====================
|
|
|
+ openAvatarDialog() {
|
|
|
+ const pageLoading = this.$loading({
|
|
|
+ lock: true,
|
|
|
+ text: '正在开辟安全云传输通道...',
|
|
|
+ spinner: 'el-icon-loading',
|
|
|
+ background: 'rgba(255, 255, 255, 0.7)'
|
|
|
+ })
|
|
|
+ getAvatarChannelInfo().then(res => {
|
|
|
+ if (res.code === 0) {
|
|
|
+ this.imgData.channelCode = res.data.channelCode
|
|
|
+ this.imgOssUrl = res.data.ossUrl
|
|
|
+ this.imgHeaders.Authorization = 'Bearer ' + res.data.token
|
|
|
+ this.avatarDialogVisible = true
|
|
|
+ } else {
|
|
|
+ this.$message.error(res.msg)
|
|
|
+ }
|
|
|
+ }).catch(error => {
|
|
|
+ this.$message.error(error.message)
|
|
|
+ }).finally(() => {
|
|
|
+ pageLoading.close()
|
|
|
+ })
|
|
|
+ },
|
|
|
+ beforeAvatarUpload(file) {
|
|
|
+ if (!this.imgOssUrl) {
|
|
|
+ this.$message.error('未检测到可用的云端存储节点!')
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ const isJPG = file.type === 'image/jpeg'
|
|
|
+ const isLt2M = file.size / 1024 / 1024 < 2
|
|
|
+ if (!isJPG) this.$message.error('仅允许上传 JPG 格式的图片!')
|
|
|
+ if (!isLt2M) this.$message.error('图片体积不能超过 2MB!')
|
|
|
+ return isJPG && isLt2M
|
|
|
+ },
|
|
|
+ handleAvatarSuccess(res, file) {
|
|
|
+ if (res.code === 0) {
|
|
|
+ this.previewUrl = URL.createObjectURL(file.raw)
|
|
|
+ this.uploadSuccessData = res.data
|
|
|
+ this.$message.success('新头像本地预载完成,请点击确认进行同步')
|
|
|
+ } else {
|
|
|
+ this.$message.error('文件服务器解析异常: ' + res.msg)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ confirmAvatarUpdate() {
|
|
|
+ if (!this.uploadSuccessData) {
|
|
|
+ return this.$message.warning('尚未检测到新上传的图像资源')
|
|
|
+ }
|
|
|
+ this.submitLoading = true
|
|
|
+ const postParam = {
|
|
|
+ channelCode: this.imgData.channelCode,
|
|
|
+ uploadId: this.uploadSuccessData.uploadId
|
|
|
+ }
|
|
|
+ updateAvatar(postParam).then(resp => {
|
|
|
+ if (resp.code === 0) {
|
|
|
+ this.loginUser.avatarUrl = resp.data.avatarUrl
|
|
|
+ updateAuthedUser(this.loginUser)
|
|
|
+ this.$message.success('个人头像已成功同步至云端')
|
|
|
+ this.avatarDialogVisible = false
|
|
|
+ this.clearUploadStates()
|
|
|
+ } else {
|
|
|
+ this.$message.warning('配置档案同步失败,请稍后重试')
|
|
|
+ }
|
|
|
+ }).finally(() => {
|
|
|
+ this.submitLoading = false
|
|
|
+ })
|
|
|
+ },
|
|
|
+ cancelAvatarUpdate() {
|
|
|
+ this.avatarDialogVisible = false
|
|
|
+ this.clearUploadStates()
|
|
|
+ },
|
|
|
+ clearUploadStates() {
|
|
|
+ if (this.previewUrl && this.previewUrl.startsWith('blob:')) {
|
|
|
+ URL.revokeObjectURL(this.previewUrl)
|
|
|
+ }
|
|
|
+ this.previewUrl = ''
|
|
|
+ this.uploadSuccessData = null
|
|
|
+ },
|
|
|
+ goToRole(role) { this.$message.info(`当前操作角色: ${role}`) }
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
-.dashboard-container {
|
|
|
- background-color: #f5f7fa;
|
|
|
- min-height: 100vh;
|
|
|
-}
|
|
|
+/* 基础布局样式保持不变 */
|
|
|
+.dashboard-container { background-color: #f6f8f9; min-height: 100vh; }
|
|
|
+.dashboard-main { padding: 24px; }
|
|
|
+.top-blocks-row { display: flex; gap: 20px; margin-bottom: 20px; flex-wrap: wrap; }
|
|
|
+.flex-item { flex: 1; min-width: 320px; margin-bottom: 0 !important; }
|
|
|
+.section-block { background: #ffffff; padding: 20px; border-radius: 8px; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.02); border: 1px solid #eef1f5; }
|
|
|
+.section-title { margin-top: 0; margin-bottom: 18px; color: #1f2d3d; font-size: 15px; font-weight: 600; border-left: 4px solid #1890ff; padding-left: 12px; }
|
|
|
+.btn-group { display: flex; flex-wrap: wrap; gap: 10px; }
|
|
|
+.custom-btn { border-radius: 6px; transition: all 0.2s ease; background: #f8fafc; border-color: #e2e8f0; color: #475569; }
|
|
|
+.custom-btn:hover { background: #fff; border-color: #1890ff; color: #1890ff; transform: translateY(-1px); box-shadow: 0 2px 6px rgba(24, 144, 255, 0.15); }
|
|
|
|
|
|
-.section-block {
|
|
|
- margin-bottom: 25px;
|
|
|
- background: #fff;
|
|
|
- padding: 15px 20px;
|
|
|
- border-radius: 4px;
|
|
|
- box-shadow: 0 2px 12px 0 rgba(0,0,0,0.05);
|
|
|
-}
|
|
|
+/* 资料档案卡片 */
|
|
|
+.profile-card { border-radius: 8px; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.02) !important; border: 1px solid #eef1f5; }
|
|
|
+.card-header-custom { display: flex; align-items: center; gap: 8px; font-weight: 600; color: #334155; font-size: 15px; }
|
|
|
+.card-header-custom i { color: #1890ff; font-size: 17px; }
|
|
|
+.profile-card-content { display: flex; gap: 40px; padding: 10px 0; flex-wrap: wrap; }
|
|
|
|
|
|
-.section-title {
|
|
|
- margin-top: 0;
|
|
|
- margin-bottom: 15px;
|
|
|
- color: #303133;
|
|
|
- font-size: 16px;
|
|
|
- border-left: 4px solid #409EFF;
|
|
|
- padding-left: 10px;
|
|
|
-}
|
|
|
+/* 左侧头像展示区 */
|
|
|
+.avatar-section { display: flex; flex-direction: column; align-items: center; flex-shrink: 0; width: 140px; }
|
|
|
+.avatar-wrapper { padding: 4px; border: 1px solid #e2e8f0; border-radius: 50%; background: #fff; box-shadow: 0 4px 10px rgba(0,0,0,0.03); }
|
|
|
+.user-avatar { width: 110px; height: 110px; border-radius: 50%; background-color: #f1f5f9; }
|
|
|
+.avatar-error-slot { display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; background: #f8fafc; color: #94a3b8; font-size: 36px; }
|
|
|
+.update-avatar-btn { margin-top: 16px !important; width: 100%; border-radius: 20px; box-shadow: 0 4px 10px rgba(24, 144, 255, 0.15); }
|
|
|
+
|
|
|
+/* 右侧多列网格表单及动作按钮整合 */
|
|
|
+.profile-form { flex: 1; min-width: 280px; }
|
|
|
+.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 4px 32px; }
|
|
|
|
|
|
-.btn-group .el-button {
|
|
|
- margin-right: 10px;
|
|
|
- margin-bottom: 10px;
|
|
|
+/* 核心:带操作按钮的复合型 Input */
|
|
|
+.input-with-action {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+}
|
|
|
+.input-with-action .el-input {
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+.input-with-action .el-button--text {
|
|
|
+ padding: 0;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #1890ff;
|
|
|
}
|
|
|
|
|
|
-/* 布局网格 */
|
|
|
-.card-grid {
|
|
|
- display: grid;
|
|
|
- grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
|
|
- gap: 20px;
|
|
|
+/* 独立修饰绝对不可编辑的项 */
|
|
|
+.is-readonly ::v-deep .el-input__inner {
|
|
|
+ background-color: #f1f5f9 !important;
|
|
|
+ color: #94a3b8 !important;
|
|
|
}
|
|
|
|
|
|
-.box-card {
|
|
|
- margin-bottom: 0;
|
|
|
+/* 签名大文本定制 */
|
|
|
+.full-width-item { grid-column: 1 / -1; margin-top: 12px; }
|
|
|
+.signature-item { position: relative; }
|
|
|
+.signature-actions {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ gap: 10px;
|
|
|
+ margin-top: 6px;
|
|
|
}
|
|
|
|
|
|
-/* 工具类样式 */
|
|
|
-.w-70 {
|
|
|
- width: 70% !important;
|
|
|
+/* 全局统一下降级 Input 的只读底色质感 */
|
|
|
+::v-deep .el-input__inner,
|
|
|
+::v-deep .el-textarea__inner {
|
|
|
+ border-color: #e2e8f0 !important;
|
|
|
+ color: #334155 !important;
|
|
|
+ background-color: #ffffff; /* 允许编辑的框默认亮白 */
|
|
|
}
|
|
|
-.ml-10 {
|
|
|
- margin-left: 10px !important;
|
|
|
+::v-deep .el-input__inner[readonly],
|
|
|
+::v-deep .el-textarea__inner[readonly] {
|
|
|
+ background-color: #f8fafc !important; /* 处于只读状态时柔和变灰 */
|
|
|
}
|
|
|
+::v-deep .el-form-item__label { color: #64748b; font-weight: 500; }
|
|
|
+
|
|
|
+/* 弹窗及云上传通用 */
|
|
|
+.avatar-upload-box { display: flex; justify-content: center; padding: 10px 0 5px 0; }
|
|
|
+.avatar-uploader ::v-deep .el-upload { border: 2px dashed #cbd5e1; border-radius: 50%; cursor: pointer; position: relative; overflow: hidden; width: 140px; height: 140px; background-color: #f8fafc; transition: all 0.25s ease; }
|
|
|
+.avatar-uploader ::v-deep .el-upload:hover { border-color: #1890ff; background-color: #f0f7ff; }
|
|
|
+.upload-placeholder { display: flex; flex-direction: column; justify-content: center; align-items: center; height: 100%; color: #64748b; }
|
|
|
+.upload-placeholder i { font-size: 28px; margin-bottom: 6px; color: #94a3b8;}
|
|
|
+.upload-placeholder p { margin: 0; font-size: 13px; }
|
|
|
+.upload-preview-wrapper { position: relative; width: 140px; height: 140px; }
|
|
|
+.upload-preview { width: 140px; height: 140px; object-fit: cover; border-radius: 50%; }
|
|
|
+.upload-mask { position: absolute; inset: 0; background: rgba(15, 23, 42, 0.6); color: #fff; display: flex; flex-direction: column; gap: 6px; justify-content: center; align-items: center; font-size: 12px; border-radius: 50%; opacity: 0; transition: opacity 0.25s ease; }
|
|
|
+.upload-preview-wrapper:hover .upload-mask { opacity: 1; }
|
|
|
+.upload-tip-text { text-align: center; font-size: 12px; color: #64748b; margin-top: 12px; }
|
|
|
+
|
|
|
+/* 统一高级圆角弹窗 */
|
|
|
+::v-deep .custom-dialog { border-radius: 12px; overflow: hidden; }
|
|
|
</style>
|