Initial commit: freedom-web frontend project

This commit is contained in:
张家训
2026-08-19 17:59:00 +08:00
commit 0131747f90
186 changed files with 18882 additions and 0 deletions
+114
View File
@@ -0,0 +1,114 @@
<template>
<div ref="dom"></div>
</template>
<script>
import echarts from 'echarts'
import { on, off } from '@/libs/tools'
export default {
name: 'serviceRequests',
data () {
return {
dom: null
}
},
methods: {
resize () {
this.dom.resize()
}
},
mounted () {
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
}
},
grid: {
top: '3%',
left: '1.2%',
right: '1%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'category',
boundaryGap: false,
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
}
],
yAxis: [
{
type: 'value'
}
],
series: [
{
name: '运营商/网络服务',
type: 'line',
stack: '总量',
areaStyle: { normal: {
color: '#2d8cf0'
} },
data: [120, 132, 101, 134, 90, 230, 210]
},
{
name: '银行/证券',
type: 'line',
stack: '总量',
areaStyle: { normal: {
color: '#10A6FF'
} },
data: [257, 358, 278, 234, 290, 330, 310]
},
{
name: '游戏/视频',
type: 'line',
stack: '总量',
areaStyle: { normal: {
color: '#0C17A6'
} },
data: [379, 268, 354, 269, 310, 478, 358]
},
{
name: '餐饮/外卖',
type: 'line',
stack: '总量',
areaStyle: { normal: {
color: '#4608A6'
} },
data: [320, 332, 301, 334, 390, 330, 320]
},
{
name: '快递/电商',
type: 'line',
stack: '总量',
label: {
normal: {
show: true,
position: 'top'
}
},
areaStyle: { normal: {
color: '#398DBF'
} },
data: [820, 645, 546, 745, 872, 624, 258]
}
]
}
this.$nextTick(() => {
this.dom = echarts.init(this.$refs.dom)
this.dom.setOption(option)
on(window, 'resize', this.resize)
})
},
beforeDestroy () {
off(window, 'resize', this.resize)
}
}
</script>
+143
View File
@@ -0,0 +1,143 @@
<template>
<div>
<!-- <Row :gutter="20">-->
<!-- <i-col :xs="12" :md="8" :lg="4" v-for="(info, i) in homeInfoData" :key="`infor-${i}`"-->
<!-- style="height: 120px;padding-bottom: 10px;">-->
<!-- <infor-card shadow :color="info.color" :icon="info.icon" :icon-size="36">-->
<!-- <count-to :end="info.count" count-class="count-style"/>-->
<!-- <p>{{ info.title }}</p>-->
<!-- </infor-card>-->
<!-- </i-col>-->
<!-- </Row>-->
<!-- <Row :gutter="20" style="margin-top: 10px;">-->
<!-- <i-col :md="24" :lg="10" style="margin-bottom: 20px;">-->
<!-- <Card shadow>-->
<!-- <div ref="myChart" style="height: 400px;"></div>-->
<!-- </Card>-->
<!-- </i-col>-->
<!-- <i-col :md="24" :lg="14" style="margin-bottom: 20px;">-->
<!-- <Card shadow>-->
<!-- <Notice></Notice>-->
<!-- </Card>-->
<!-- </i-col>-->
<!-- </Row>-->
</div>
</template>
<script>
import InforCard from '_c/info-card'
import CountTo from '_c/count-to'
import {ChartPie, ChartBar} from '_c/charts'
import Example from './example.vue'
import Notice from './notice.vue'
import myTheme from '_c/charts/theme.json'
import { getQuotaCountList, getHomeInfoCountList } from '@/api/kpi/kpi-quota-library'
export default {
name: 'home',
components: {
InforCard,
CountTo,
ChartPie,
ChartBar,
Example,
Notice
},
data() {
return {
homeInfoData: [
{title: '系统用户', icon: 'md-person', count: 0, color: '#2d8cf0'},
{title: '访问人次', icon: 'md-locate', count: 0, color: '#19be6b'},
{title: '考核指标', icon: 'ios-leaf', count: 0, color: '#ff9900'},
{title: '考核规则', icon: 'ios-quote', count: 0, color: '#ed3f14'},
{title: '资产名单', icon: 'ios-paper', count: 0, color: '#E46CBB'},
{title: '工作动态', icon: 'md-megaphone', count: 0, color: '#9A66E4'}
],
}
},
methods:{
getDataList(){
// 首页info统计
getHomeInfoCountList().then(res =>{
let temp = res.data.data.list
let homeInfoData = this.homeInfoData
this.homeInfoData[0].count = temp[0].userCount
this.homeInfoData[1].count = temp[0].loginCount
this.homeInfoData[2].count = temp[0].quotaCount
this.homeInfoData[3].count = temp[0].itemCount
this.homeInfoData[4].count = temp[0].assetsCount
this.homeInfoData[5].count = temp[0].noticeCount
})
// 绘制统计图
getQuotaCountList().then(res =>{
let chartDiv = this.$refs.myChart
this.$echarts.registerTheme('myTheme', myTheme)
let myChart = this.$echarts.init(chartDiv,'myTheme')
let legendList = res.data.data.legendList
let seriesList = res.data.data.seriesList
let option = {
title: {
text: '考核指标累计使用情况',
subtext: '以对应指标使用次数为统计参数',
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)'
},
legend: {
type: 'scroll',
orient: 'vertical',
left: 'left',
data: legendList
},
series: [
{
name: '考核指标',
type: 'pie',
radius: '55%',
center: ['50%', '60%'],
data: seriesList,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
myChart.setOption(option)
console.log(res.data.data.list)
})
},
},
mounted() {
// 获取数据列表
//this.getDataList();
}
}
</script>
<style lang="less">
.count-style {
font-size: 50px;
}
/* 设置滚动条的样式 */
::-webkit-scrollbar {
width:12px;
background-color: #f0f0f0;
}
/* 滚动槽 */
::-webkit-scrollbar-track {
border-radius:10px;
}
/* 滚动条滑块 */
::-webkit-scrollbar-thumb {
border-radius:5px;
background: #d8d7d7;
}
</style>
+2
View File
@@ -0,0 +1,2 @@
import home from './home.vue'
export default home
+15
View File
@@ -0,0 +1,15 @@
<template>
<div>
消息中心
</div>
</template>
<script>
export default {
name: "message-center"
}
</script>
<style scoped>
</style>
+164
View File
@@ -0,0 +1,164 @@
<template>
<div>
<Row>
<Col span="24">
<!-- 公告区域 start -->
<div>
<Table :height="360"
ref="noticeTable"
highlight-row
:columns="noticeListColumns"
:data="noticeDataList"
:loading="noticeTableLoading"
@on-row-click="toDetail">
</Table>
<div class="pagination">
<Page show-sizer
show-total
size="small"
:total="pageTotal"
:page-size="pageParam.pageSize"
:current="pageParam.pageNum"
@on-page-size-change="changePageSize"
@on-change="changePage">
</Page>
</div>
</div>
</Col>
</Row>
<!-- 公告区域 end -->
<!-- Modal区域 start -->
<Modal v-model="noticeModal"
fullscreen
:mask-closable="false"
:footer-hide="true">
<Row :gutter="24">
<Col span="20">
<h1>{{ notice.title }}</h1>
</Col>
<Divider orientation="right" style="font-size: 14px;color: #999;">
<span style="padding-right: 24px">时间:{{ notice.publishTime | formatDateToYyMMDd }} </span>
<span>来源:{{ notice.ssoUser.nickname }}</span>
</Divider>
<Col span="20">
<p class="ql-editor" v-html="notice.content">{{ notice.content }}</p>
</Col>
</Row>
</Modal>
<!-- Modal区域 end -->
</div>
</template>
<script>
import { getDataList} from '@/api/basic/basic-notice'
import { formatDateToYyMMDd, objectUtil, } from "@/common/js/util"
// quill 编辑器 样式
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
export default {
name: "notice",
data() {
return {
fixedHeader: true,//高度自适应
autoHeight: `${window.innerHeight} ` * 0.30,//获取当前可视高度-定义Table高度
noticeTableLoading: false,
noticeDataList: [],
pageTotal: 0, //总页数
pageSize: 10, //每页显示条数
pageCurrent: 1, //当前页
pageParam: { //调用时候的分页参数
pageNum: 1,
pageSize: 10
},
noticeListColumns: [
{
title: "#",
type: 'index',
width: 60,
align: 'center',
className: 'my-thead',
indexMethod: (row) => {
return (row._index + 1) + ((this.pageParam.pageNum - 1) * this.pageParam.pageSize);
}
},
{
title: '工作动态',
key: 'title',
className: 'my-thead'
},
],
noticeModal: false,
noticeModalTitle: '',
notice: {
title: '',
isPublish: '1',
content: '',
ssoUser: {},
status: '1',
},
}
},
filters: {
formatDateToYyMMDd(value) {
return formatDateToYyMMDd(value);
},
},
methods: {
getDataList() {
let temp = JSON.parse(JSON.stringify(this.pageParam))
let data = objectUtil.trimJoin(temp,{"isPublish": 1} )
this.noticeTableLoading = true
getDataList(data).then(res => {
let pageInfo = res.data.data.pageInfo
this.pageTotal = pageInfo.total
this.noticeDataList = pageInfo.list
// 延迟300毫秒,给它更流畅的感觉
setTimeout(() => {
this.noticeTableLoading = false
}, 300)
})
},
changePage(index) {
// 需要显示开始数据的index,(因为数据是从0开始的,页码是从1开始的,需要-1)
this.pageParam.pageNum = index;
this.getDataList();
},
changePageSize(page) {
// 切换分页条数
this.pageParam.pageNum = 1;
this.pageParam.pageSize = page;
this.getDataList();
},
toDetail(index) {
this.notice.title = index.title;
this.notice.publishTime = index.publishTime;
this.notice.content = index.content;
this.notice.ssoUser = index.ssoUser;
this.noticeModal = true;
}
},
mounted() {
this.getDataList()
}
}
</script>
<style lang="less">
.ivu-table th.my-thead {
background: #6790bc;
color: #ffffff;
}
.ivu-table-row{
cursor:pointer
}
.ql-editor .ql-video {
width: 700px;
height: 500px;
}
</style>
+239
View File
@@ -0,0 +1,239 @@
<template>
<div>
<Row>
<Col span="8">
<div style="width:240px">
<Menu active-name="basicInfo" theme="light" @on-select="toChangeMenu">
<MenuItem name="basicInfo">基本信息</MenuItem>
<MenuItem name="changePassword">修改密码</MenuItem>
</Menu>
</div>
</Col>
<Col span="16">
<div v-if="currentMenu == 'basicInfo'">
<div class="title">
<h1>基本信息</h1>
</div>
<Form :rules="userRuleCustom" :model="userFormItem" ref="userFormItem" :label-width="120"
label-position="right">
<Row :gutter="32">
<Col span="32">
<FormItem label="头像" prop="avatar">
<uploadAvatar ref="uploadAvatar"></uploadAvatar>
</FormItem>
</Col>
<Col span="32">
<FormItem label="登录账号" prop="username">
{{ userFormItem.username }}
</FormItem>
</Col>
<Col span="32">
<FormItem label="用户昵称" prop="nickname">
<Input type="text" class="form-input" v-model="userFormItem.nickname" placeholder="请输入用户昵称"/>
</FormItem>
</Col>
<Col span="32">
<FormItem label="联系电话" prop="phone">
<Input type="text" class="form-input" v-model="userFormItem.phone" placeholder="请输入联系电话"/>
</FormItem>
</Col>
<Col span="32">
<FormItem label="电子邮箱" prop="email">
<Input type="text" class="form-input" v-model="userFormItem.email" placeholder="请输入电子邮箱"/>
</FormItem>
</Col>
<Col span="32">
<FormItem label="个性签名" prop="signature">
<Input type="textarea" class="form-input" v-model="userFormItem.signature" :rows="4" maxlength="100"
show-word-limit placeholder="请输入个性签名"/>
</FormItem>
</Col>
<Col span="32">
<FormItem>
<Button type="primary"
:loading="userSubmitLoading"
@click="handleSubmit('userFormItem')">保存并生效
</Button>
</FormItem>
</Col>
</Row>
</Form>
</div>
<div v-if="currentMenu == 'changePassword'">
<div class="title">
<h1>修改密码</h1>
</div>
<Form :rules="changePasswordRuleCustom" :model="changePasswordFormItem" ref="changePasswordFormItem"
:label-width="120" label-position="right">
<Row :gutter="32">
<Col span="32">
<FormItem label="登录账号" prop="username">
{{ userFormItem.username }}
</FormItem>
</Col>
<Col span="32">
<FormItem label="原密码" prop="oldPassword">
<Input type="password" password class="form-input" v-model="changePasswordFormItem.oldPassword"
placeholder="请输入原密码"/>
</FormItem>
</Col>
<Col span="32">
<FormItem label="新密码" prop="newPassword">
<Input type="password" password class="form-input" v-model="changePasswordFormItem.newPassword"
placeholder="请输入新密码"/>
</FormItem>
</Col>
<Col span="32">
<FormItem>
<Button type="primary"
:loading="changePasswordSubmitLoading"
@click="handleChangePasswordSubmit('changePasswordFormItem')">保存并生效
</Button>
</FormItem>
</Col>
</Row>
</Form>
</div>
</Col>
</Row>
</div>
</template>
<script>
import {getPersonalInfo, update, updatePassword} from '@/api/sys/sys-user'
import uploadAvatar from "@/components/my-components/upload-avatar"
import {mapActions} from "vuex";
export default {
name: "personal-center",
components: {
uploadAvatar
},
data() {
return {
currentMenu: 'basicInfo',
userSubmitLoading: false,
userFormItem: {
id: '',
username: '',
nickname: '',
avatar: '',
phone: '',
email: '',
signature: '',
status: '1',
},
userRuleCustom: {
username: [{
required: true, message: '请输入登录账号', trigger: 'blur'
}],
nickname: [{
required: true, message: '请输入用户昵称', trigger: 'blur'
}],
phone: [{
required: true, message: '请输入联系电话', trigger: 'blur'
}],
email: [{
required: true, message: '请输入联系电话', trigger: 'blur'
}],
},
changePasswordSubmitLoading: false,
changePasswordFormItem: {
id: '',
oldPassword: '',
newPassword: '',
},
changePasswordRuleCustom: {
oldPassword: [{
required: true, message: '请输入原密码', trigger: 'blur'
}],
newPassword: [{
required: true, message: '请输入新密码', trigger: 'blur'
}],
},
}
},
methods: {
...mapActions([
'handleLogOut'
]),
toChangeMenu(name) {
this.currentMenu = name
},
getPersonalInfo() {
getPersonalInfo().then(res => {
let temp = res.data.data.entity
this.userFormItem.id = temp.id
this.userFormItem.avatar = temp.avatar
let avatar = temp.avatar
if (temp.avatar === null || temp.avatar === "") {
avatar = "https://732128096-read.oss-cn-hangzhou.aliyuncs.com/avatar.png"
}
this.$refs.uploadAvatar.setData(temp.id, avatar);
this.userFormItem.username = temp.username
this.userFormItem.nickname = temp.nickname
this.userFormItem.phone = temp.phone
this.userFormItem.email = temp.email
this.userFormItem.signature = temp.signature
//设置当前密码修改框的值
this.changePasswordFormItem.id = temp.id
})
},
handleSubmit(userFormItem) {
// 个人中心修改保存
this.$refs[userFormItem].validate((valid) => {
if (valid) {
this.userSubmitLoading = true
let data = this.userFormItem
update(data).then(res => {
setTimeout(() => {
this.userSubmitLoading = false
this.$Message.success("个人信息保存并生效!")
}, 300)
})
}
})
},
handleChangePasswordSubmit(changePasswordFormItem) {
// 个人中心密码保存
this.$refs[changePasswordFormItem].validate((valid) => {
if (valid) {
this.changePasswordSubmitLoading = true
let data = this.changePasswordFormItem
updatePassword(data).then(res => {
setTimeout(() => {
this.changePasswordSubmitLoading = false
if (res.data.code === 419) {
this.$Message.warning(res.data.message)
} else {
this.$Message.success("修改密码保存并生效!")
this.handleLogOut().then(() => {
this.$router.push({
name: 'login'
})
})
}
}, 300)
})
}
})
}
},
created() {
this.getPersonalInfo()
},
}
</script>
<style lang="less" scoped>
.title {
margin: 10px 5px 20px 0;
}
.form-input {
width: 280px
}
</style>