feat:新增账号设置模块和引入表格组件

This commit is contained in:
Xwen
2024-01-08 23:02:35 +08:00
parent a0fd35bf5e
commit 10a2593979
11 changed files with 2111 additions and 1579 deletions

View File

@@ -11,6 +11,7 @@
"@element-plus/icons-vue": "^2.1.0",
"@microsoft/signalr": "^8.0.0",
"axios": "^1.3.4",
"dayjs": "^1.11.10",
"echarts": "^5.4.2",
"element-plus": "^2.2.32",
"highlight": "^0.2.4",
@@ -1846,9 +1847,9 @@
"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
},
"node_modules/dayjs": {
"version": "1.11.7",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.7.tgz",
"integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ=="
"version": "1.11.10",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.10.tgz",
"integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
},
"node_modules/debug": {
"version": "4.3.4",
@@ -5456,9 +5457,9 @@
"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
},
"dayjs": {
"version": "1.11.7",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.7.tgz",
"integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ=="
"version": "1.11.10",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.10.tgz",
"integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
},
"debug": {
"version": "4.3.4",

View File

@@ -12,6 +12,7 @@
"@element-plus/icons-vue": "^2.1.0",
"@microsoft/signalr": "^8.0.0",
"axios": "^1.3.4",
"dayjs": "^1.11.10",
"echarts": "^5.4.2",
"element-plus": "^2.2.32",
"highlight": "^0.2.4",

View File

@@ -73,3 +73,45 @@ export function getLoginCode() {
method: "get",
});
}
/**
* 第三方账号登录
* @param {*} params 参数
* @param {*} scheme 类型
* @returns
*/
export function authOtherLogin(params, scheme) {
return request({
url: `/auth/oauth/login/${scheme}`,
method: "get",
params: params,
});
}
/**
* 第三方账号绑定
* @param {*} params 参数
* @param {*} scheme 类型
* @returns
*/
export function authOtherBind(params, scheme) {
return request({
url: `/auth/oauth/bind/${scheme}`,
method: "post",
params: params,
});
}
/**
* 第三方账号绑定
* @param {*} params 参数
* @param {*} scheme 类型
* @returns
*/
export function getOtherAuthInfo(params) {
return request({
url: `auth/account`,
method: "get",
params: params,
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -0,0 +1,128 @@
<script setup>
import dayjs from "dayjs";
defineProps({
col: {
// Column
type: Object,
default: () => {
return {};
},
},
});
const emit = defineEmits(["command"]);
// 按钮组事件
const handleAction = (command, scope) => {
emit("command", command, scope.row);
};
</script>
<template>
<!-- 如果有配置多级表头的数据则递归该组件 -->
<template v-if="col.children?.length">
<el-table-column
:label="col.label"
:width="col.width"
:align="col.align"
v-bind="col"
>
<TableColumn
v-for="(item, index) in col.children"
:col="item"
:key="index"
/>
</el-table-column>
</template>
<!-- 其他正常列 -->
<el-table-column
v-else
:show-overflow-tooltip="col.showOverflowTooltip"
:sortable="col.sortable"
:prop="col.prop"
:align="col.align"
:label="col.label"
:width="col.width"
v-bind="col"
>
<template #default="{ row, $index }">
<!---图片 (START)-->
<!-- 如需更改图片size可自行配置参数 -->
<el-image
v-if="col.type === 'image'"
preview-teleported
:hide-on-click-modal="true"
:preview-src-list="[row[col.prop]]"
:src="row[col.prop]"
fit="cover"
class="w-9 h-9 rounded-lg"
/>
<!---图片 (END)-->
<!--- 格式化日期 (本项目日期是时间戳这里日期格式化可根据你的项目来更改) (START)-->
<template v-else-if="col.type === 'date'">
<!---十位数时间戳-->
<span v-if="String(row[col.prop])?.length <= 10">
{{
row[col.prop]
? dayjs.unix(row[col.prop]).format(col.dateFormat ?? "YYYY-MM-DD")
: ""
}}
</span>
<!---十三位数时间戳-->
<span v-else>{{
dayjs(row[col.prop]).format(col.dateFormat ?? "YYYY-MM-DD")
}}</span>
</template>
<!--- 格式化日期 (本项目日期是时间戳这里日期格式化可根据你的项目来更改) (END)-->
<!-- 如果传递按钮数组就展示按钮组 START-->
<!-- v-permission 给按钮添加权限 []则不设置权限-->
<el-button-group v-else-if="col.buttons?.length">
<el-button
v-for="(btn, index) in col.buttons"
:key="index"
:size="btn.size || 'small'"
:type="btn.type"
@click="handleAction(btn.command, { row, $index })"
:disabled="btn.disabled || false"
v-permission="btn.permission || []"
>{{ btn.name }}</el-button
>
</el-button-group>
<!-- 如果传递按钮数组就展示按钮组 END-->
<!-- render函数 (START) -->
<!-- 使用内置的component组件可以支持h函数渲染和txs语法 -->
<component
v-else-if="col.render"
:is="col.render"
:row="row"
:index="$index"
/>
<!-- render函数 (END) -->
<!-- 自定义slot (START) -->
<slot
v-else-if="col.slot"
:slotName="col.slot"
:row="row"
:index="$index"
></slot>
<!-- 自定义slot (END) -->
<!-- 默认渲染 (START) -->
<span v-else>{{ row[col.prop] }}</span>
<!-- 默认渲染 (END) -->
</template>
<!-- 自定义表头 -->
<template #header="{ column, $index }">
<component
v-if="col.headerRender"
:is="col.headerRender"
:column="column"
:index="$index"
/>
<slot
v-else-if="col.headerSlot"
name="customHeader"
:slotName="col.headerSlot"
:column="column"
:index="$index"
></slot>
<span v-else>{{ column.label }}</span>
</template>
</el-table-column>
</template>

View File

@@ -0,0 +1,206 @@
<template>
<div class="yi-table">
<el-table
ref="tableRef"
:data="tableData"
v-bind="_options"
header-row-class-name="header-row-class-name"
@selection-change="handleSelectionChange"
@row-click="handleRowClick"
@cell-click="handleCellClick"
@sort-change="handleSortChange"
>
<template v-for="(col, index) in columns">
<!---复选框, 序号 (START)-->
<el-table-column
:key="index"
v-if="
col.type === 'index' ||
col.type === 'selection' ||
col.type === 'expand'
"
:sortable="col.sortable"
:prop="col.prop"
:align="col.align"
:label="col.label"
:type="col.type"
:index="indexMethod"
:width="col.width"
v-bind="col"
/>
<!---复选框, 序号 (END)-->
<TableColumn
:key="index + '_key'"
:col="col"
v-else
@command="handleAction"
>
<template #default="{ slotName, row, index }">
<slot :name="slotName" :row="row" :index="index" />
</template>
<!-- 自定义表头插槽 -->
<template #customHeader="{ slotName, column, index }">
<slot :name="slotName" :column="column" :index="index" />
</template>
</TableColumn>
</template>
</el-table>
</div>
<!-- 分页器 -->
<div
v-if="_options.showPagination"
class="table-pagination"
:class="[`pagination_${_options.paginationPosition}`]"
>
<el-pagination
v-bind="_paginationConfig"
@size-change="pageSizeChange"
@current-change="currentPageChange"
/>
</div>
</template>
<script setup name="YiTable">
import { computed, ref } from "vue";
import TableColumn from "./TableColumn.vue";
const props = defineProps({
tableData: {
// table的数据
type: Array,
default: () => {
return [];
},
},
columns: {
type: Array,
default: () => {
return [];
},
},
options: {
type: Object,
default: () => {
return {};
},
},
});
const tableRef = ref();
// 设置option默认值如果传入自定义的配置则合并option配置项
const _options = computed(() => {
const option = {
stripe: false,
tooltipEffect: "dark",
showHeader: true,
showPagination: false,
paginationPosition: "right",
rowStyle: () => "cursor:pointer", // 行样式
// headerRowClassName: 'table-header-row',
};
return Object.assign(option, props?.options);
});
// 合并分页配置
const _paginationConfig = computed(() => {
const config = {
total: 0,
currentPage: 1,
pageSize: 10,
pageSizes: [10, 20, 50],
background: true,
layout: "total, sizes, prev, pager, next, jumper",
};
return Object.assign(config, _options.value.paginationConfig);
});
const emit = defineEmits([
"selection-change", // 当选择项发生变化时会触发该事件
"row-click", // 当某一行被点击时会触发该事件
"cell-click", // 当某个单元格被点击时会触发该事件
"command", // 按钮组事件
"size-change", // pageSize事件
"current-change", // currentPage按钮组事件
"pagination-change", // currentPage或者pageSize改变触发
"sort-change", // 列排序发生改变触发
]);
// 自定义索引
const indexMethod = (index) => {
const tabIndex =
index +
(_paginationConfig.value.currentPage - 1) *
_paginationConfig.value.pageSize +
1;
return tabIndex;
};
// 切换pageSize
const pageSizeChange = (pageSize) => {
emit("size-change", pageSize);
emit("pagination-change", 1, pageSize);
};
// 切换currentPage
const currentPageChange = (currentPage) => {
emit("current-change", currentPage);
emit("pagination-change", currentPage, _paginationConfig.value.pageSize);
};
// 按钮组事件
const handleAction = (command, row) => {
emit("command", command, row);
};
// 多选事件
const handleSelectionChange = (val) => {
emit("selection-change", val);
};
// 当某一行被点击时会触发该事件
const handleRowClick = (row, column, event) => {
emit("row-click", row, column, event);
};
// 当某个单元格被点击时会触发该事件
const handleCellClick = (row, column, cell, event) => {
emit("cell-click", row, column, cell, event);
};
/**
* 当表格的排序条件发生变化的时候会触发该事件
* 在列中设置 sortable 属性即可实现以该列为基准的排序, 接受一个 Boolean默认为 false。
* 可以通过 Table 的 default-sort 属性设置默认的排序列和排序顺序。
* 如果需要后端排序,需将 sortable 设置为 custom同时在 Table 上监听 sort-change 事件,
* 在事件回调中可以获取当前排序的字段名和排序顺序,从而向接口请求排序后的表格数据。
*/
const handleSortChange = ({ column, prop, order }) => {
emit("sort-change", { column, prop, order });
};
// 暴露给父组件参数和方法,如果外部需要更多的参数或者方法,都可以从这里暴露出去。
defineExpose({ element: tableRef });
</script>
<style lang="scss" scoped>
:deep(.el-image__inner) {
transition: all 0.3s;
cursor: pointer;
&:hover {
transform: scale(1.2);
}
}
.yi-table {
flex: 1;
position: relative;
overflow: hidden;
.el-table {
height: 100%;
position: absolute;
}
}
.table-pagination {
margin-top: 20px;
display: flex;
align-items: center;
}
.table-pagination.pagination_left {
justify-content: flex-start;
}
.table-pagination.pagination_center {
justify-content: center;
}
.table-pagination.pagination_right {
justify-content: flex-end;
}
</style>

View File

@@ -3,8 +3,9 @@
</template>
<script setup>
import { ref, watch } from "vue";
import { ref, watch, onMounted } from "vue";
import { useRoute } from "vue-router";
import { authOtherLogin, authOtherBind } from "@/apis/auth.js";
const route = useRoute();
@@ -13,8 +14,14 @@ const code = ref(route.query.code);
const message = ref("");
watch(
() => code.value,
(val) => {
async (val) => {
if (val) {
// 使用正则表达式提取路由参数
const regex = /\/auth\/([\w-]+)[?]?/;
const result = regex.exec(route.fullPath);
const authParam = result != null ? result[1].toUpperCase() : null;
const res = await authOtherBind({ code: val }, authParam);
console.log(res, "res");
message.value = "授权成功";
// window.close();
}

View File

@@ -0,0 +1,143 @@
<template>
<div class="account-box">
<div class="title">您可以绑定以下第三方账号</div>
<div class="image-list">
<div class="item" @click="handleQQLogin">
<div class="image">
<img src="@/assets/login_images/qq-setting.png" alt="" />
</div>
<div class="text">QQ</div>
</div>
<div class="item" @click="handleGiteeLogin">
<div class="image">
<img src="@/assets/login_images/gitee-setting.png" alt="" />
</div>
<div class="text">Gitee</div>
</div>
</div>
<div class="title">使用以下任意方式都可以登录到您的意社区账号</div>
<div class="table">
<yi-table
:table-data="tableData"
:options="tableOptions"
:columns="tableColumn"
></yi-table>
</div>
</div>
</template>
<script setup>
import { computed, onMounted } from "vue";
import { getOtherAuthInfo } from "@/apis/auth.js";
import YiTable from "@/components/yi-table/index.vue";
import useUserStore from "@/stores/user";
const userInfo = useUserStore();
const tableOptions = computed(() => {
return {
showPagination: false,
border: true,
};
});
const tableColumn = [
{
type: "index",
minWidth: "50",
label: "序号",
align: "center",
},
{
prop: "timeInterval",
label: "绑定账号信息",
minWidth: "110",
align: "center",
},
{
prop: "timeInterval",
label: "详情",
minWidth: "110",
align: "center",
},
{
prop: "timeInterval",
label: "绑定时间",
minWidth: "110",
align: "center",
},
{
prop: "timeInterval",
label: "状态",
minWidth: "110",
align: "center",
},
{
label: "操作",
align: "center",
minWidth: "200",
fixed: "right",
buttons: [
{
name: "绑定",
type: "text",
command: "edit",
},
],
},
];
onMounted(async () => {
const { data } = await getOtherAuthInfo({ userId: userInfo.id });
});
const handleQQLogin = () => {
window.open(
"https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=102087446&redirect_uri=https://ccnetcore.com/auth/qq&state=true&scope=get_user_info",
undefined,
"width=500,height=500,left=50,top=50"
);
};
const handleGiteeLogin = () => {
window.open(
"https://gitee.com/oauth/authorize?client_id=949f3519969adc5cfe82c209b71300e8e0868e8536f3d7f59195c8f1e5b72502&redirect_uri=https%3A%2F%2Fccnetcore.com%2Fauth%2Fgitee&response_type=code",
undefined,
"width=500,height=500,left=50,top=50"
);
};
</script>
<style lang="scss" scoped>
.account-box {
.title {
font-size: 20px;
font-weight: bold;
}
.image-list {
margin: 10px;
display: flex;
.item {
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
margin-right: 20px;
.image {
width: 30px;
height: 30px;
img {
width: 100%;
height: 100%;
}
}
}
}
.table {
margin-top: 10px;
:deep(.yi-table) {
width: 100%;
height: 200px;
}
}
}
</style>

View File

@@ -61,6 +61,9 @@
<el-tab-pane label="修改密码" name="resetPwd">
<resetPwd />
</el-tab-pane>
<el-tab-pane label="账号设置" name="accountSetting">
<accountSet />
</el-tab-pane>
</el-tabs>
</el-card>
</el-col>
@@ -72,6 +75,7 @@
import userAvatar from "./UserAvatar.vue";
import userInfo from "./UserInfo.vue";
import resetPwd from "./ResetPwd.vue";
import accountSet from "./AccountSetting.vue";
import { getUserProfile } from "@/apis/userApi.js";
import { onMounted, ref, reactive } from "vue";

File diff suppressed because it is too large Load Diff