忘了写到哪了

This commit is contained in:
quantulr
2022-08-30 10:36:30 +08:00
parent 1d2f37bfe1
commit 9e00dce03c
44 changed files with 5136 additions and 304 deletions

View File

@ -17,12 +17,15 @@
"dependencies": {
"@element-plus/icons-vue": "1.1.4",
"@vueuse/core": "8.5.0",
"@wangeditor/editor": "^5.1.14",
"@wangeditor/editor-for-vue": "^5.1.12",
"axios": "0.26.1",
"echarts": "5.3.2",
"element-plus": "2.1.8",
"file-saver": "2.0.5",
"fuse.js": "6.5.3",
"js-cookie": "3.0.1",
"js-md5": "^0.7.3",
"jsencrypt": "3.2.1",
"nprogress": "0.2.0",
"pinia": "2.0.14",

64
src/api/config.js Normal file
View File

@ -0,0 +1,64 @@
import request from "@/utils/request";
export function launch(data) {
return request({
url: "/v1/message/launch",
method: "post",
data,
});
}
// 省市区选择
export function areaList(params) {
return request({
url: "/enterprise/v1/config/area",
method: "get",
params,
});
}
// 领域
export function industry(params) {
return request({
url: "/enterprise/v1/config/industry",
method: "get",
params,
});
}
// 省列表
export function provinceList() {
return request({
url: "/region/allProvince",
method: "get",
});
}
// 市列表
export function cityList(provinceCode) {
return request({
url: "/region/allCity",
method: "get",
params: {
provinceCode,
},
});
}
// 县区列表
export function districtList(cityCode) {
return request({
url: "/region/allArea",
method: "get",
params: {
cityCode,
},
});
}
// 获取领域列表
export function listSysIndustry(query) {
return request({
url: "/business/sysIndustry/list",
method: "get",
params: query,
});
}

View File

@ -0,0 +1,53 @@
import request from "@/utils/request";
// 个人信息录入
export const insertClientUser = (data) => {
return request({
url: "/app/insertClientUser",
method: "POST",
data,
});
};
// 企业信息修改
export const updateEnterprise = (data) => {
return request({
url: "/app/updateEnterprise",
method: "POST",
data,
});
};
// 企业服务需求列表
export const demandList = (params) => {
return request({
url: "/app/demandList",
method: "GET",
params,
});
};
// 产品列表
export const getEnterpriseProduct = (params) => {
return request({
url: "/app/getEnterpriseProduct",
method: "GET",
params,
});
};
// 新增产品
export const insertEnterpriseProduct = (data) => {
return request({
url: "/app/insertEnterpriseProduct",
method: "POST",
data,
});
};
// 根据id获取产品信息
export const getProductById = (params) => {
return request({
url: "/app/getProductById",
method: "GET",
params,
});
};

76
src/api/identity/index.js Normal file
View File

@ -0,0 +1,76 @@
import request from "@/utils/request";
// 首页每一项
export function identity() {
return request({
url: "/app/getRoleStatus",
});
}
// 当前状态
export function settled() {
return request({
url: "/enterprise/v1/settled",
});
}
// 当前状态切换
export function identitySwitch(data) {
return request({
url: "/enterprise/v1/user/identity/switch",
method: "post",
data,
});
}
// 申请企业入住
export function insertEnterprise(data) {
return request({
url: "/app/insertEnterprise",
method: "post",
data,
});
}
// 所属单位
export function researchSelect() {
return request({
url: "/enterprise/v1/manage/research/select",
});
}
// 所属单位>实验室
export function laboratorySelect() {
return request({
url: "/enterprise/v1/manage/research/laboratory",
});
}
// 专家入驻
export function expert(data) {
return request({
url: "/enterprise/v1/settled/expert",
method: "post",
data,
});
}
// 科研机构入驻
export function research(data) {
return request({
url: "/enterprise/v1/settled/research",
method: "post",
data,
});
}
// 实验室入驻
export function laboratory(data) {
return request({
url: "/enterprise/v1/settled/laboratory",
method: "post",
data,
});
}
// 实验室入驻
export function agent(data) {
return request({
url: "/enterprise/v1/settled/agent",
method: "post",
data,
});
}

View File

@ -1,4 +1,4 @@
import request from '@/utils/request'
import request from "@/utils/request";
// 登录方法
export function login(username, password, code, uuid) {
@ -6,54 +6,65 @@ export function login(username, password, code, uuid) {
username,
password,
code,
uuid
}
uuid,
};
return request({
url: '/login',
url: "/app/login",
headers: {
isToken: false
isToken: false,
},
method: 'post',
data: data
})
method: "post",
data: data,
});
}
// 注册方法
export function register(data) {
return request({
url: '/register',
url: "/app/register",
headers: {
isToken: false
isToken: false,
},
method: 'post',
data: data
})
method: "post",
data: data,
});
}
// 获取用户详细信息
export function getInfo() {
return request({
url: '/getInfo',
method: 'get'
})
url: "/app/info",
method: "get",
});
}
// 退出方法
export function logout() {
return request({
url: '/logout',
method: 'post'
})
url: "/logout",
method: "post",
});
}
// 获取验证码
export function getCodeImg() {
return request({
url: '/captchaImage',
url: "/captchaImage",
headers: {
isToken: false
isToken: false,
},
method: 'get',
timeout: 20000
})
}
method: "get",
timeout: 20000,
});
}
// 重置密码
export function resetPassword() {
return request({
url: "/captchaImage",
headers: {
isToken: false,
},
method: "get",
timeout: 20000,
});
}

BIN
src/assets/images/login.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -80,7 +80,7 @@ const dialogImageUrl = ref("");
const dialogVisible = ref(false);
const baseUrl = import.meta.env.VITE_APP_BASE_API;
const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传的图片服务器地址
const headers = ref({ Authorization: "Bearer " + getToken() });
const headers = ref({ 'client-token': "Bearer " + getToken() });
const fileList = ref([]);
const showTip = computed(
() => props.isShowTip && (props.fileType || props.fileSize)

View File

@ -0,0 +1,345 @@
<template>
<div>
<el-upload
:headers="headers"
:action="videoUploadUrl"
list-type="picture-card"
:on-success="handleSuccess"
:on-error="handleUploadError"
:before-upload="handleBeforeUpload"
:file-list="fileList"
:on-progress="handleProgress"
:data="data"
accept="video/mp4"
:class="{ hide: fileList.length >= limit }"
>
<!-- accept="video/mp4, video/ogg, video/flv,video/avi,video/wmv,video/rmvb" -->
<!-- :disabled="fileList.length >= limit || uploadBtn" -->
<template #default>
<el-icon><Plus /></el-icon>
</template>
<template #file="{ file }">
<div style="height: 100%">
<video
class="el-upload-list__item-thumbnail"
:src="file.url"
alt=""
></video>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handleShowVideo(file)"
>
<el-icon><video-play /></el-icon>
</span>
<!-- <span
class="el-upload-list__item-edit"
@click="handleEditVideo(file)"
>
<el-icon><Edit /></el-icon>
</span> -->
<span
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<el-icon><delete /></el-icon>
</span>
</span>
<el-progress
type="circle"
class="progressModule"
:color="colors"
:percentage="Number(uploadPercentage)"
v-if="showProgress && file.url == uploadUrl"
></el-progress>
</div>
</template>
</el-upload>
<!-- 上传提示 -->
<div class="el-upload__tip" v-if="showTip">
请上传
<template v-if="fileSize">
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
</template>
<template v-if="fileType">
格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
</template>
的文件
</div>
<el-dialog v-model="dialogVisible" title="预览" append-to-body width="40%">
<video
:src="dialogVideoUrl"
alt=""
autoplay
class="video"
controls="controls"
></video>
</el-dialog>
<!-- <el-dialog v-model="editView" width="40%" append-to-body>
<el-input
type="textarea"
:rows="4"
v-model="editForm.url"
@input="editVideo"
></el-input>
</el-dialog> -->
</div>
</template>
<script>
import { getToken } from "@/utils/auth";
import {
ElMessage,
ElMessageBox,
ElNotification,
ElLoading,
} from "element-plus";
export default {
props: {
modelValue: [String, Object, Array],
// 数量限制
limit: {
type: Number,
default: 1,
},
// 大小限制(MB)
fileSize: {
type: Number,
default: 5,
},
// 文件类型, 例如['png', 'jpg', 'jpeg']
fileType: {
type: Array,
default: () => ["doc", "xls", "ppt", "txt", "pdf"],
},
// 是否显示提示
isShowTip: {
type: Boolean,
default: true,
},
data: {
type: Object,
default: () => {
return {};
},
},
},
computed: {
showTip() {
return this.isShowTip && (this.fileType || this.fileSize);
},
},
watch: {
modelValue: {
handler(val) {
if (val) {
// 首先将值转为数组
const list = Array.isArray(val) ? val : this.modelValue.split(",");
// 然后将数组转为对象数组
this.fileList = list.map((item) => {
if (typeof item === "string") {
item = { name: item, url: item };
// if (item.indexOf(this.baseUrl) === -1) {
// item = { name: this.baseUrl + item, url: this.baseUrl + item };
// } else {
// item = { name: item, url: item };
// }
}
return item;
});
} else {
this.fileList = [];
return [];
}
},
deep: true,
immediate: true,
},
},
data() {
return {
// 设置上传的请求头部
headers: { "x-token": getToken() },
baseUrl: import.meta.env.VITE_APP_BASE_API,
videoUploadUrl:
import.meta.env.VITE_APP_BASE_API + "/enterprise/v1/upload",
dialogVideoUrl: "",
dialogVisible: false,
fileList: [],
editForm: {
url: "",
uid: null,
},
editView: false,
uploadPercentage: 0,
showProgress: false,
uploadUrl: "",
colors: [
{ color: "#ADD8E6", percentage: 20 },
{ color: "#87CEEB", percentage: 40 },
{ color: "#87CEFA", percentage: 60 },
{ color: "#00BFFF", percentage: 80 },
{ color: "#1296DB", percentage: 100 },
],
uploadBtn: false,
loading: null,
};
},
methods: {
// 移除视频
handleRemove(file) {
const findex = this.fileList.map((f) => f.url).indexOf(file.url);
if (findex > -1) {
this.fileList.splice(findex, 1);
this.submitFile();
}
},
handleShowVideo(file) {
this.dialogVideoUrl = file.url;
this.dialogVisible = true;
},
handleBeforeUpload(file) {
if (this.fileType.length) {
let fileExtension = "";
if (file.name.lastIndexOf(".") > -1) {
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
}
const isTypeOk = this.fileType.some((type) => {
if (file.type.indexOf(type) > -1) return true;
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
return false;
});
if (!isTypeOk) {
ElMessage.error(
`文件格式不正确, 请上传${this.fileType.join("/")}格式文件!`
);
return false;
}
}
// 校检文件大小
if (this.fileSize) {
const isLt = file.size / 1024 / 1024 < this.fileSize;
if (!isLt) {
ElMessage.error(`上传文件大小不能超过 ${this.fileSize} MB!`);
return false;
}
}
this.loading = ElLoading.service({
lock: true,
text: "Loading",
background: "rgba(0, 0, 0, 0.7)",
});
},
handleUploadError() {
if (this.loading) {
this.loading.close();
}
},
// 上传完成
handleSuccess(response, file, fileList) {
this.showProgress = false;
this.uploadBtn = false;
if (response.code != "200") {
for (var i = 0; i < fileList.length; i++) {
if (i + 1 == fileList.length) {
fileList.splice(i, 1);
}
}
ElMessage.error(response.message);
this.loading.close();
return;
} else {
ElMessage.success(response.message);
let obj = {
name: response.data.filename,
status: "success",
uid: file.uid,
url: response.data.url,
};
this.fileList.push(obj);
this.loading.close();
this.submitFile();
}
},
// 播放视频
handleEditVideo(file) {
this.editForm.url = file.url;
this.editForm.uid = file.uid;
this.editView = true;
},
// 编辑视频
editVideo() {
for (let i in this.fileList) {
if (this.fileList[i].uid == this.editForm.uid) {
this.fileList[i].url = this.editForm.url;
}
}
this.submitFile();
},
submitFile() {
// this.$emit("submitImg", this.fileList);
this.$emit("update:modelValue", this.listToString(this.fileList));
},
// 对象转成指定字符串分隔
listToString(list, separator) {
let strs = "";
separator = separator || ",";
for (let i in list) {
strs += list[i].url.replace(this.baseUrl, "") + separator;
}
return strs != "" ? strs.substr(0, strs.length - 1) : "";
},
// 上传进度
handleProgress(response, file, fileList) {
this.uploadBtn = true;
this.uploadUrl = file.url;
this.showProgress = true;
this.uploadPercentage = file.percentage.toFixed(0);
},
},
};
</script>
<style lang="scss" scoped>
.el-icon-plus {
font-size: 30px !important;
}
.el-icon-edit {
font-size: 18px !important;
}
.el-icon-video-play {
font-size: 18px !important;
}
.el-icon-delete {
font-size: 18px !important;
color: rgb(243, 143, 130);
}
.el-input >>> .el-textarea__inner {
font-size: 18px !important;
}
.video {
display: block;
margin: auto;
min-height: 200px;
max-height: 600px;
min-width: 200px;
max-width: 100%;
}
.progressModule >>> .el-progress__text {
color: #1296db;
font-size: 15px !important;
}
:deep(.hide .el-upload--picture-card) {
display: none;
}
// 去掉动画效果
:deep(.el-list-enter-active),
:deep(.el-list-leave-active) {
transition: all 0s;
}
:deep(.el-list-enter, .el-list-leave-active) {
opacity: 0;
transform: translateY(0);
}
</style>

View File

@ -0,0 +1,113 @@
<template>
<div :style="`border: 1px solid #ccc; width: ${width}px`">
<Toolbar
style="border-bottom: 1px solid #ccc"
:editor="editorRef"
:defaultConfig="toolbarConfig"
:mode="mode"
/>
<Editor
:style="`min-height: ${minHeight}px; height: ${height}px; overflow-y: hidden`"
v-model="valueHtml"
:defaultConfig="editorConfig"
:mode="mode"
@onCreated="handleCreated"
@onChange="handleChange"
/>
</div>
</template>
<script>
import "@wangeditor/editor/dist/css/style.css"; // 引入 css
import { getToken } from "@/utils/auth";
import { onBeforeUnmount, ref, shallowRef, onMounted, toRefs } from "vue";
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
const baseUrl = import.meta.env.VITE_APP_BASE_API;
export default {
components: { Editor, Toolbar },
props: {
modelValue: {
type: String,
default: "",
},
minHeight: {
type: [String, Number],
default: 300,
},
height: {
type: [String, Number],
default: 300,
},
width: {
type: [String, Number],
default: 820,
},
mode: {
type: String,
default: "default", // or 'simple'
},
},
setup(props, context) {
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef();
// 内容 HTML
const valueHtml = ref("");
watch(
() => props.modelValue,
(val) => {
valueHtml.value = val;
},
{ immediate: true }
);
const { height } = toRefs(props);
const toolbarConfig = {
excludeKeys: [],
};
const editorConfig = {
placeholder: "请输入内容...",
MENU_CONF: {
uploadImage: {
server: `${baseUrl}/common/upload`,
// 自定义增加 http header
fieldName: "file",
headers: {
Authorization: `Bearer ${getToken()}`,
},
customInsert(res, insertFn) {
// res 即服务端的返回结果
console.log(res);
// 从 res 中找到 url alt href ,然后插图图片
insertFn(res.url, null, null);
},
},
},
};
// console.log(editor.getMenuConfig('uploadImage'));
// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value;
if (editor == null) return;
editor.destroy();
});
const handleCreated = (editor) => {
editorRef.value = editor; // 记录 editor 实例,重要!
};
const handleChange = (editor) => {
context.emit("update:modelValue", editor.getHtml());
};
return {
editorRef,
valueHtml,
mode: "default", // 或 'simple'
toolbarConfig,
editorConfig,
height,
handleCreated,
handleChange,
};
},
};
</script>

View File

@ -0,0 +1,273 @@
<template>
<div class="webHead">
<el-row>
<el-col :span="8">
<img src="@/assets/logo/logo.png" class="logo" />
</el-col>
<el-col :span="16">
<ul class="menu">
<li class="menu-item1">
<div
class="menu-item-tit"
:class="pagePath == '/' ? 'active' : ''"
@click="handlePath('/')"
>
首页
</div>
</li>
<li class="menu-item1 solution">
<div
class="menu-item-tit"
:class="pagePath.indexOf('/solution/') != -1 ? 'active' : ''"
>
解决方案
</div>
<div class="show_box">
<div
class="pointer"
:class="pagePath == '/solution/small' ? 'x_blue _active' : ''"
@click="handlePath('/solution/small')"
>
中小型企业服务
</div>
<div
class="pointer"
:class="pagePath == '/solution/large' ? 'x_blue _active' : ''"
@click="handlePath('/solution/large')"
>
大型企业服务
</div>
<div
class="pointer"
:class="
pagePath == '/solution/government' ? 'x_blue _active' : ''
"
@click="handlePath('/solution/government')"
>
政府区域服务
</div>
<div
class="pointer"
:class="
pagePath == '/solution/scientific' ? 'x_blue _active' : ''
"
@click="handlePath('/solution/scientific')"
>
科研区域服务
</div>
</div>
</li>
<li class="menu-item1">
<div
class="menu-item-tit"
:class="pagePath == '/innovate' ? 'active' : ''"
@click="handlePath('/innovate')"
>
创新服务
</div>
</li>
<li class="menu-item1">
<div
class="menu-item-tit"
:class="pagePath == '/activity' ? 'active' : ''"
@click="handlePath('/activity')"
>
活动报名
</div>
</li>
<li class="menu-item1">
<div
class="menu-item-tit"
:class="pagePath == '/about' ? 'active' : ''"
@click="handlePath('/about')"
>
关于我们
</div>
</li>
<li class="menu-item1" style="display: flex; justify-content: center">
<div
v-if="!userStore.avatar"
class="menu-item-tit"
:class="pagePath == '/login' ? 'active' : ''"
@click="handlePath('/login')"
>
登录注册
</div>
<el-dropdown
v-else
style="margin-top: 20px"
class="avatar-container right-menu-item hover-effect"
trigger="click"
>
<div class="avatar-wrapper">
<img :src="userStore.avatar" class="user-avatar" />
<i class="el-icon-caret-bottom" />
</div>
<template #dropdown>
<el-dropdown-menu>
<!-- <router-link to="/identity/index"> -->
<el-dropdown-item @click="handlePage"
>个人中心</el-dropdown-item
>
<!-- </router-link> -->
<el-dropdown-item divided @click="logout">
<span>退出登录</span>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</li>
</ul>
</el-col>
</el-row>
</div>
</template>
<script setup>
import { ElMessageBox } from "element-plus";
import { defineComponent, onMounted, reactive, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import useUserStore from "@/store/modules/user";
const userStore = useUserStore();
let state = reactive({});
let pagePath = ref("");
const route = useRoute();
const router = useRouter();
watch(
() => route.path,
(newVal, oldVal) => {
pagePath.value = newVal;
}
);
pagePath.value = route.path;
function handlePage() {
// router.push("/identity/index");
// let routeData = "";
// const selectRole = localStorage.getItem("select_identity");
// if (selectRole > 0) {
// routeData = router.resolve({ path: "/admin" });
// } else {
// routeData = router.resolve({ path: "/identity/index" });
// }
// window.open(routeData.href, "_blank");
const routeData = router.resolve({ path: "/identity/index" });
window.open(routeData.href, "_blank");
}
function handlePath(path) {
pagePath.value = path;
router.push(path);
}
function logout() {
ElMessageBox.confirm("确定注销并退出系统吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
useUserStore()
.logOut()
.then(() => {
location.href = "/";
});
})
.catch(() => {});
}
</script>
<style lang="scss">
ol,
ul,
dl {
margin: 0;
padding: 0;
}
li,
dd,
dt {
padding: 0;
margin: 0;
list-style: none;
}
.webHead {
position: fixed;
z-index: 2001;
top: 0;
left: 0;
width: 100%;
height: 80px;
padding: 0 260px;
box-sizing: border-box;
background: #ffffff;
box-shadow: 0px 0px 43px 0px rgba(0, 42, 255, 0.09);
.logo {
height: 30px;
margin: 25px 0;
}
.menu {
display: flex;
.solution {
position: relative;
.show_box {
display: none;
position: absolute;
top: 80px;
width: 100%;
text-align: center;
background-color: red;
div {
height: 42px;
line-height: 42px;
font-size: 14px;
color: #666666;
background-color: #f2f6ff;
}
._active {
background-color: #fff;
}
}
&:hover {
.show_box {
display: block;
}
}
}
.menu-item1 {
flex: 1;
text-align: center;
.menu-item-tit {
display: inline-block;
padding: 25px 0;
font-size: 16px;
font-family: Source Han Sans CN;
font-weight: 400;
color: #000;
cursor: pointer;
}
.menu-item-tit:hover {
opacity: 0.8;
}
.active {
font-size: 20px;
font-weight: 500;
border-bottom: 2.5px solid #000;
}
}
}
}
</style>
<style lang="scss" scoped>
.user-avatar {
cursor: pointer;
width: 40px;
height: 40px;
border-radius: 50%;
}
</style>

View File

@ -0,0 +1,67 @@
<template>
<div class="login-code">
<el-button
class="x_btns login-code-img"
size="default"
type="primary"
style="width: 100%"
@click.prevent="getCode"
:disabled="isDisabled"
>
<span>{{ buttonName }}</span>
</el-button>
</div>
</template>
<script setup>
import { getCodeImg } from "@/api/login";
const props = defineProps({
mobile: String,
});
const disabled = ref(true);
const buttonName = ref("获取验证码");
const isDisabled = ref(false);
const time = ref(60);
const { proxy } = getCurrentInstance();
function getCode() {
const reg = /^1[3|4|5|6|7|8|9][0-9]\d{8}$/;
if (!reg.test(props.mobile))
return proxy.$modal.msgError("请输入正确的手机号码");
isDisabled.value = true;
let interval = setInterval(function () {
buttonName.value = time.value + "后重新发送";
--time.value;
if (time.value < 0) {
buttonName.value = "重新发送";
time.value = 60;
isDisabled.value = false;
clearInterval(interval);
}
}, 1000);
getCodeImg({ mobile: props.mobile })
.then((res) => {})
.catch(() => {
isDisabled.value = false;
clearInterval(interval);
});
}
</script>
<style lang="scss" scoped>
.login-code {
width: 30%;
height: 38px;
float: right;
img {
cursor: pointer;
vertical-align: middle;
}
}
.login-code-img {
height: 36px;
}
</style>

View File

@ -1,63 +1,73 @@
import router from './router'
import { ElMessage } from 'element-plus'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { getToken } from '@/utils/auth'
import { isHttp } from '@/utils/validate'
import { isRelogin } from '@/utils/request'
import useUserStore from '@/store/modules/user'
import useSettingsStore from '@/store/modules/settings'
import usePermissionStore from '@/store/modules/permission'
import router from "./router";
import { ElMessage } from "element-plus";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
import { getToken } from "@/utils/auth";
import { isHttp } from "@/utils/validate";
import { isRelogin } from "@/utils/request";
import useUserStore from "@/store/modules/user";
import useSettingsStore from "@/store/modules/settings";
import usePermissionStore from "@/store/modules/permission";
NProgress.configure({ showSpinner: false });
const whiteList = ['/login', '/auth-redirect', '/bind', '/register'];
const whiteList = ["/login", "/auth-redirect", "/bind", "/register"];
router.beforeEach((to, from, next) => {
NProgress.start()
NProgress.start();
if (getToken()) {
to.meta.title && useSettingsStore().setTitle(to.meta.title)
to.meta.title && useSettingsStore().setTitle(to.meta.title);
/* has token*/
if (to.path === '/login') {
next({ path: '/' })
NProgress.done()
if (to.path === "/login") {
next({ path: "/" });
NProgress.done();
} else {
if (useUserStore().roles.length === 0) {
isRelogin.show = true
isRelogin.show = true;
// 判断当前用户是否已拉取完user_info信息
useUserStore().getInfo().then(() => {
isRelogin.show = false
usePermissionStore().generateRoutes().then(accessRoutes => {
// 根据roles权限生成可访问的路由表
accessRoutes.forEach(route => {
if (!isHttp(route.path)) {
router.addRoute(route) // 动态添加可访问路由表
}
})
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
useUserStore()
.getInfo()
.then(() => {
isRelogin.show = false;
// next({ ...to, replace: true });
usePermissionStore()
.generateRoutes(useUserStore().roleId)
.then((accessRoutes) => {
console.log(accessRoutes);
// 根据roles权限生成可访问的路由表
accessRoutes.forEach((route) => {
if (!isHttp(route.path)) {
router.addRoute(route); // 动态添加可访问路由表
}
});
next({ ...to, replace: true }); // hack方法 确保addRoutes已完成
});
})
}).catch(err => {
useUserStore().logOut().then(() => {
ElMessage.error(err)
next({ path: '/' })
})
})
.catch((err) => {
console.log(err);
useUserStore()
.logOut()
.then(() => {
ElMessage.error(err);
next({ path: "/" });
});
});
} else {
next()
next();
}
}
} else {
// 没有token
if (whiteList.indexOf(to.path) !== -1) {
// 在免登录白名单,直接进入
next()
next();
} else {
next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
NProgress.done()
next(`/login?redirect=${to.fullPath}`); // 否则全部重定向到登录页
NProgress.done();
}
}
})
});
router.afterEach(() => {
NProgress.done()
})
NProgress.done();
});

View File

@ -1,6 +1,7 @@
import { createWebHistory, createRouter } from 'vue-router'
import { createWebHistory, createRouter } from "vue-router";
/* Layout */
import Layout from '@/layout'
import Layout from "@/layout";
import basicInfo from "../views/admin/enterprise/account/basicInfo.vue";
/**
* Note: 路由配置项
@ -27,147 +28,502 @@ import Layout from '@/layout'
// 公共路由
export const constantRoutes = [
{
path: '/redirect',
path: "/",
name: "Home",
component: () => import("../views/website/website-layout.vue"),
hidden: true,
children: [
{
path: "login",
name: "login",
component: () => import("@/views/website/login/index.vue"),
children: [],
},
{
path: "",
component: () => import("../views/website/home/index.vue"),
},
],
},
{
path: "/redirect",
component: Layout,
hidden: true,
children: [
{
path: '/redirect/:path(.*)',
component: () => import('@/views/redirect/index.vue')
}
]
},
{
path: '/login',
component: () => import('@/views/login'),
hidden: true
},
{
path: '/register',
component: () => import('@/views/register'),
hidden: true
path: "/redirect/:path(.*)",
component: () => import("@/views/redirect/index.vue"),
},
],
},
// {
// path: "/login",
// component: () => import("@/views/login"),
// hidden: true,
// },
// {
// path: "/register",
// component: () => import("@/views/register"),
// hidden: true,
// },
{
path: "/:pathMatch(.*)*",
component: () => import('@/views/error/404'),
hidden: true
component: () => import("@/views/error/404"),
hidden: true,
},
{
path: '/401',
component: () => import('@/views/error/401'),
hidden: true
path: "/401",
component: () => import("@/views/error/401"),
hidden: true,
},
{
path: '',
component: Layout,
redirect: '/index',
path: "/identity",
component: () => import("@/views/identity/layout"),
hidden: true,
children: [
{
path: '/index',
component: () => import('@/views/index'),
name: 'Index',
meta: { title: '首页', icon: 'dashboard', affix: true }
}
]
path: "index",
component: () => import("@/views/identity/index"),
name: "index",
meta: { title: "身份选择" },
},
{
path: "enterprise",
component: () => import("@/views/identity/enterprise"),
name: "enterprise",
meta: { title: "企业入驻" },
},
// {
// path:'expert',
// component: () => import('@/views/identity/expert'),
// name: 'expert',
// meta: { title: '专家入驻'}
// },
// {
// path:'research',
// component: () => import('@/views/identity/research'),
// name: 'research',
// meta: { title: '研究机构入驻'}
// },
// {
// path:'laboratory',
// component: () => import('@/views/identity/laboratory'),
// name: 'laboratory',
// meta: { title: '实验室入驻'}
// },
// {
// path:'agent',
// component: () => import('@/views/identity/agent'),
// name: 'agent',
// meta: { title: '科技经纪人入驻'}
// }
],
},
{
path: '/user',
path: "/admin",
component: Layout,
redirect: "/admin/index",
children: [
{
path: "index",
component: () => import("@/views/admin/index"),
name: "Index",
meta: { title: "首页", icon: "dashboard", affix: true },
},
],
},
// {
// path: '',
// component: Layout,
// redirect: '/index',
// children: [
// {
// path: '/index',
// component: () => import('@/views/index'),
// name: 'Index',
// meta: { title: '首页', icon: 'dashboard', affix: true }
// }
// ]
// },
{
path: "/user",
component: Layout,
hidden: true,
redirect: 'noredirect',
redirect: "noredirect",
children: [
{
path: 'profile',
component: () => import('@/views/system/user/profile/index'),
name: 'Profile',
meta: { title: '个人中心', icon: 'user' }
}
]
}
]
path: "profile",
component: () => import("@/views/system/user/profile/index"),
name: "Profile",
meta: { title: "个人中心", icon: "user" },
},
],
},
];
// 企业后台路由表
export const enterpriseRoutes = [
{
path: "/account",
component: Layout,
meta: { title: "账号管理", icon: "dashboard" },
alwaysShow: true,
redirect: "/account/basicInfo",
children: [
{
path: "basicInfo",
component: basicInfo,
name: "basicInfo",
meta: { title: "基本资料", icon: "list" },
},
{
path: "bill",
component: () => import("@/views/admin/enterprise/account/bill"),
name: "Bill",
meta: { title: "账单列表", icon: "list" },
},
{
path: "record",
component: () => import("@/views/admin/enterprise/account/record"),
name: "Record",
meta: { title: "创新币兑换记录", icon: "list" },
},
],
},
{
path: "/extension",
component: Layout,
meta: { title: "产品推广", icon: "dashboard" },
alwaysShow: true,
redirect: "/extension/product",
children: [
{
path: "product",
component: () => import("@/views/admin/enterprise/extension/product"),
name: "Product",
meta: { title: "产品列表", icon: "list" },
},
{
path: "release",
component: () => import("@/views/admin/enterprise/extension/release"),
hidden: true,
name: "Release",
meta: { title: "发布产品", icon: "list" },
},
{
path: "results",
component: () => import("@/views/admin/enterprise/extension/results"),
hidden: true,
name: "Results",
meta: { title: "匹配结果", icon: "list" },
},
{
path: "business",
component: () => import("@/views/admin/enterprise/extension/business"),
hidden: true,
name: "Business",
meta: { title: "浏览信息", icon: "list" },
},
],
},
{
path: "/demand",
component: Layout,
meta: { title: "需求管理", icon: "dashboard" },
alwaysShow: true,
redirect: "/demand/serviceDemand",
children: [
{
path: "serviceDemand",
component: () =>
import("@/views/admin/enterprise/demand/serviceDemand"),
name: "serviceDemand",
meta: { title: "服务需求", icon: "list" },
},
{
path: "release",
component: () => import("@/views/admin/enterprise/demand/release"),
hidden: true,
name: "release",
meta: { title: "发布服务需求", icon: "list" },
},
{
path: "technology",
component: () => import("@/views/admin/enterprise/demand/technology"),
name: "technology",
meta: { title: "技术需求", icon: "list" },
},
{
path: "released",
component: () => import("@/views/admin/enterprise/demand/released"),
hidden: true,
name: "released",
meta: { title: "发布技术需求", icon: "list" },
},
{
path: "results",
component: () => import("@/views/admin/enterprise/demand/results"),
hidden: true,
name: "results",
meta: { title: "匹配结果", icon: "list" },
},
],
},
{
path: "/activity",
component: Layout,
meta: { title: "活动管理", icon: "dashboard" },
alwaysShow: true,
redirect: "/activity/active",
children: [
{
path: "active",
component: () => import("@/views/admin/enterprise/activity/active"),
name: "Active",
meta: { title: "活动列表", icon: "list" },
},
],
},
];
// 专家后台路由表
export const expertRoutes = [
{
path: "/account",
component: Layout,
meta: { title: "zj账号管理", icon: "dashboard" },
alwaysShow: true,
// redirect: 'noRedirect',
redirect: "/account/bill",
children: [
{
path: "bill",
component: () => import("@/views/admin/expert/account/bill"),
name: "Bill",
meta: { title: "账单列表", icon: "list" },
},
{
path: "exchange",
component: () => import("@/views/admin/expert/account/exchange"),
name: "Exchange",
meta: { title: "兑换记录", icon: "list" },
},
{
path: "basicInfo",
component: () => import("@/views/admin/expert/account/basicInfo"),
name: "basicInfo",
meta: { title: "基本资料", icon: "list" },
},
{
path: "transactionAuth",
component: () => import("@/views/admin/expert/account/transactionAuth"),
name: "transactionAuth",
meta: { title: "交易认证", icon: "list" },
},
{
path: "profit",
component: () => import("@/views/admin/expert/account/profit"),
name: "profit",
meta: { title: "收益管理", icon: "list" },
},
{
path: "withdrawal",
component: () => import("@/views/admin/expert/account/withdrawal"),
hidden: true,
name: "Withdrawal",
meta: { title: "提现记录", icon: "list" },
},
],
},
{
path: "/technology",
component: Layout,
meta: { title: "技术管理", icon: "dashboard" },
alwaysShow: true,
redirect: "/technology/achievement",
children: [
{
path: "achievement",
component: () => import("@/views/admin/expert/technology/achievement"),
name: "Achievement",
meta: { title: "成果列表", icon: "list" },
},
{
path: "release",
component: () => import("@/views/admin/expert/technology/release"),
hidden: true,
name: "Release",
meta: { title: "发布成果", icon: "list" },
},
{
path: "results",
component: () => import("@/views/admin/expert/technology/results"),
hidden: true,
name: "Results",
meta: { title: "匹配结果", icon: "list" },
},
{
path: "patent",
component: () => import("@/views/admin/expert/technology/patent"),
name: "patent",
meta: { title: "专利列表", icon: "list" },
},
{
path: "claimPatent",
component: () => import("@/views/admin/expert/technology/claimPatent"),
hidden: true,
name: "claimPatent",
meta: { title: "认领专利", icon: "list" },
},
{
path: "paper",
component: () => import("@/views/admin/expert/technology/paper"),
name: "Paper",
meta: { title: "论文列表", icon: "list" },
},
{
path: "research",
component: () => import("@/views/admin/expert/technology/research"),
name: "Research",
meta: { title: "科研项目列表", icon: "list" },
},
],
},
{
path: "/demand",
component: Layout,
meta: { title: "需求管理", icon: "dashboard" },
alwaysShow: true,
redirect: "/demand/serviceDemand",
children: [
{
path: "serviceDemand",
component: () => import("@/views/admin/expert/demand/serviceDemand"),
name: "serviceDemand",
meta: { title: "服务需求", icon: "list" },
},
{
path: "release",
component: () => import("@/views/admin/expert/demand/release"),
hidden: true,
name: "release",
meta: { title: "发布需求", icon: "list" },
},
{
path: "already",
component: () => import("@/views/admin/expert/demand/already"),
name: "already",
meta: { title: "已合作企业", icon: "list" },
},
{
path: "think",
component: () => import("@/views/admin/expert/demand/think"),
name: "think",
meta: { title: "想合作企业", icon: "list" },
},
],
},
{
path: "/activity",
component: Layout,
meta: { title: "活动管理", icon: "dashboard" },
alwaysShow: true,
redirect: "/activity/active",
children: [
{
path: "active",
component: () => import("@/views/admin/expert/activity/active"),
name: "Active",
meta: { title: "活动列表", icon: "list" },
},
],
},
];
// 动态路由,基于用户权限动态去加载
export const dynamicRoutes = [
{
path: '/system/user-auth',
path: "/system/user-auth",
component: Layout,
hidden: true,
permissions: ['system:user:edit'],
permissions: ["system:user:edit"],
children: [
{
path: 'role/:userId(\\d+)',
component: () => import('@/views/system/user/authRole'),
name: 'AuthRole',
meta: { title: '分配角色', activeMenu: '/system/user' }
}
]
path: "role/:userId(\\d+)",
component: () => import("@/views/system/user/authRole"),
name: "AuthRole",
meta: { title: "分配角色", activeMenu: "/system/user" },
},
],
},
{
path: '/system/role-auth',
path: "/system/role-auth",
component: Layout,
hidden: true,
permissions: ['system:role:edit'],
permissions: ["system:role:edit"],
children: [
{
path: 'user/:roleId(\\d+)',
component: () => import('@/views/system/role/authUser'),
name: 'AuthUser',
meta: { title: '分配用户', activeMenu: '/system/role' }
}
]
path: "user/:roleId(\\d+)",
component: () => import("@/views/system/role/authUser"),
name: "AuthUser",
meta: { title: "分配用户", activeMenu: "/system/role" },
},
],
},
{
path: '/system/dict-data',
path: "/system/dict-data",
component: Layout,
hidden: true,
permissions: ['system:dict:list'],
permissions: ["system:dict:list"],
children: [
{
path: 'index/:dictId(\\d+)',
component: () => import('@/views/system/dict/data'),
name: 'Data',
meta: { title: '字典数据', activeMenu: '/system/dict' }
}
]
path: "index/:dictId(\\d+)",
component: () => import("@/views/system/dict/data"),
name: "Data",
meta: { title: "字典数据", activeMenu: "/system/dict" },
},
],
},
{
path: '/monitor/job-log',
path: "/monitor/job-log",
component: Layout,
hidden: true,
permissions: ['monitor:job:list'],
permissions: ["monitor:job:list"],
children: [
{
path: 'index',
component: () => import('@/views/monitor/job/log'),
name: 'JobLog',
meta: { title: '调度日志', activeMenu: '/monitor/job' }
}
]
path: "index",
component: () => import("@/views/monitor/job/log"),
name: "JobLog",
meta: { title: "调度日志", activeMenu: "/monitor/job" },
},
],
},
{
path: '/tool/gen-edit',
path: "/tool/gen-edit",
component: Layout,
hidden: true,
permissions: ['tool:gen:edit'],
permissions: ["tool:gen:edit"],
children: [
{
path: 'index/:tableId(\\d+)',
component: () => import('@/views/tool/gen/editTable'),
name: 'GenEdit',
meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
}
]
}
]
path: "index/:tableId(\\d+)",
component: () => import("@/views/tool/gen/editTable"),
name: "GenEdit",
meta: { title: "修改生成配置", activeMenu: "/tool/gen" },
},
],
},
];
const router = createRouter({
history: createWebHistory(),
routes: constantRoutes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
return savedPosition;
} else {
return { top: 0 }
return { top: 0 };
}
},
});

View File

@ -6,7 +6,7 @@ export default {
/**
* 侧边栏主题 深色主题theme-dark浅色主题theme-light
*/
sideTheme: 'theme-dark',
sideTheme: 'theme-light',
/**
* 是否系统布局配置
*/

View File

@ -1,138 +1,171 @@
import auth from '@/plugins/auth'
import router, { constantRoutes, dynamicRoutes } from '@/router'
import { getRouters } from '@/api/menu'
import Layout from '@/layout/index'
import ParentView from '@/components/ParentView'
import InnerLink from '@/layout/components/InnerLink'
import auth from "@/plugins/auth";
import router, { constantRoutes, dynamicRoutes } from "@/router";
import { getRouters } from "@/api/menu";
import Layout from "@/layout/index";
import ParentView from "@/components/ParentView";
import InnerLink from "@/layout/components/InnerLink";
import { enterpriseRoutes } from "@/router";
import { expertRoutes } from "../../router";
// 匹配views里面所有的.vue文件
const modules = import.meta.glob('./../../views/**/*.vue')
const modules = import.meta.glob("./../../views/**/*.vue");
const usePermissionStore = defineStore(
'permission',
{
state: () => ({
routes: [],
addRoutes: [],
defaultRoutes: [],
topbarRouters: [],
sidebarRouters: []
}),
actions: {
setRoutes(routes) {
this.addRoutes = routes
this.routes = constantRoutes.concat(routes)
},
setDefaultRoutes(routes) {
this.defaultRoutes = constantRoutes.concat(routes)
},
setTopbarRoutes(routes) {
this.topbarRouters = routes
},
setSidebarRouters(routes) {
this.sidebarRouters = routes
},
generateRoutes(roles) {
return new Promise(resolve => {
// 向后端请求路由数据
getRouters().then(res => {
const sdata = JSON.parse(JSON.stringify(res.data))
const rdata = JSON.parse(JSON.stringify(res.data))
const defaultData = JSON.parse(JSON.stringify(res.data))
const sidebarRoutes = filterAsyncRouter(sdata)
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
const defaultRoutes = filterAsyncRouter(defaultData)
const asyncRoutes = filterDynamicRoutes(dynamicRoutes)
asyncRoutes.forEach(route => { router.addRoute(route) })
this.setRoutes(rewriteRoutes)
this.setSidebarRouters(constantRoutes.concat(sidebarRoutes))
this.setDefaultRoutes(sidebarRoutes)
this.setTopbarRoutes(defaultRoutes)
resolve(rewriteRoutes)
})
})
}
}
})
const usePermissionStore = defineStore("permission", {
state: () => ({
routes: [],
addRoutes: [],
defaultRoutes: [],
topbarRouters: [],
sidebarRouters: [],
}),
actions: {
setRoutes(routes) {
this.addRoutes = routes;
this.routes = constantRoutes.concat(routes);
console.log(routes);
},
setDefaultRoutes(routes) {
this.defaultRoutes = constantRoutes.concat(routes);
},
setTopbarRoutes(routes) {
this.topbarRouters = routes;
},
setSidebarRouters(routes) {
this.sidebarRouters = routes;
},
generateRoutes(roles) {
return new Promise((resolve) => {
let routesList = [];
if (roles == 1) {
routesList = enterpriseRoutes;
} else if (roles == 2) {
routesList = expertRoutes;
}
// console.log(routesList);
// const sdata = JSON.parse(JSON.stringify(routesList));
// const rdata = JSON.parse(JSON.stringify(routesList));
// const defaultData = JSON.parse(JSON.stringify(routesList));
// const sidebarRoutes = filterAsyncRouter(sdata);
// const rewriteRoutes = filterAsyncRouter(rdata, false, true);
// const defaultRoutes = filterAsyncRouter(defaultData);
// const asyncRoutes = filterDynamicRoutes(dynamicRoutes);
// console.log(
// sdata,
// rdata,
// defaultData,
// sidebarRoutes,
// rewriteRoutes,
// defaultRoutes,
// asyncRoutes
// );
// asyncRoutes.forEach((route) => {
// router.addRoute(route);
// });
this.setRoutes(routesList);
this.setSidebarRouters(constantRoutes.concat(routesList));
this.setDefaultRoutes(routesList);
this.setTopbarRoutes(routesList);
resolve(routesList);
// 向后端请求路由数据
// getRouters().then(res => {
// console.log(res);
// const sdata = JSON.parse(JSON.stringify(res.data))
// const rdata = JSON.parse(JSON.stringify(res.data))
// const defaultData = JSON.parse(JSON.stringify(res.data))
// const sidebarRoutes = filterAsyncRouter(sdata)
// const rewriteRoutes = filterAsyncRouter(rdata, false, true)
// const defaultRoutes = filterAsyncRouter(defaultData)
// const asyncRoutes = filterDynamicRoutes(dynamicRoutes)
// asyncRoutes.forEach(route => { router.addRoute(route) })
// this.setRoutes(rewriteRoutes)
// this.setSidebarRouters(constantRoutes.concat(sidebarRoutes))
// this.setDefaultRoutes(sidebarRoutes)
// this.setTopbarRoutes(defaultRoutes)
// resolve(rewriteRoutes)
// })
});
},
},
});
// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
return asyncRouterMap.filter(route => {
return asyncRouterMap.filter((route) => {
if (type && route.children) {
route.children = filterChildren(route.children)
route.children = filterChildren(route.children);
}
if (route.component) {
// Layout ParentView 组件特殊处理
if (route.component === 'Layout') {
route.component = Layout
} else if (route.component === 'ParentView') {
route.component = ParentView
} else if (route.component === 'InnerLink') {
route.component = InnerLink
if (route.component === "Layout") {
route.component = Layout;
} else if (route.component === "ParentView") {
route.component = ParentView;
} else if (route.component === "InnerLink") {
route.component = InnerLink;
} else {
route.component = loadView(route.component)
route.component = loadView(route.component);
}
}
if (route.children != null && route.children && route.children.length) {
route.children = filterAsyncRouter(route.children, route, type)
route.children = filterAsyncRouter(route.children, route, type);
} else {
delete route['children']
delete route['redirect']
delete route["children"];
delete route["redirect"];
}
return true
})
return true;
});
}
function filterChildren(childrenMap, lastRouter = false) {
var children = []
var children = [];
childrenMap.forEach((el, index) => {
if (el.children && el.children.length) {
if (el.component === 'ParentView' && !lastRouter) {
el.children.forEach(c => {
c.path = el.path + '/' + c.path
if (el.component === "ParentView" && !lastRouter) {
el.children.forEach((c) => {
c.path = el.path + "/" + c.path;
if (c.children && c.children.length) {
children = children.concat(filterChildren(c.children, c))
return
children = children.concat(filterChildren(c.children, c));
return;
}
children.push(c)
})
return
children.push(c);
});
return;
}
}
if (lastRouter) {
el.path = lastRouter.path + '/' + el.path
el.path = lastRouter.path + "/" + el.path;
}
children = children.concat(el)
})
return children
children = children.concat(el);
});
return children;
}
// 动态路由遍历,验证是否具备权限
export function filterDynamicRoutes(routes) {
const res = []
routes.forEach(route => {
const res = [];
routes.forEach((route) => {
if (route.permissions) {
if (auth.hasPermiOr(route.permissions)) {
res.push(route)
res.push(route);
}
} else if (route.roles) {
if (auth.hasRoleOr(route.roles)) {
res.push(route)
res.push(route);
}
}
})
return res
});
return res;
}
export const loadView = (view) => {
let res;
for (const path in modules) {
const dir = path.split('views/')[1].split('.vue')[0];
const dir = path.split("views/")[1].split(".vue")[0];
if (dir === view) {
res = () => modules[path]();
}
}
return res;
}
};
export default usePermissionStore
export default usePermissionStore;

View File

@ -1,70 +1,87 @@
import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
import defAva from '@/assets/images/profile.jpg'
import { login, logout, getInfo } from "@/api/login";
import { getToken, setToken, removeToken } from "@/utils/auth";
import defAva from "@/assets/images/profile.jpg";
const useUserStore = defineStore(
'user',
{
state: () => ({
token: getToken(),
name: '',
avatar: '',
roles: [],
permissions: []
}),
actions: {
// 登录
login(userInfo) {
const username = userInfo.username.trim()
const password = userInfo.password
const code = userInfo.code
const uuid = userInfo.uuid
return new Promise((resolve, reject) => {
login(username, password, code, uuid).then(res => {
setToken(res.token)
this.token = res.token
resolve()
}).catch(error => {
reject(error)
const useUserStore = defineStore("user", {
state: () => ({
token: getToken(),
name: "",
avatar: "",
roles: [],
permissions: [],
roleId: localStorage.getItem("role-id") ?? 1,
}),
actions: {
// 登录
login(userInfo) {
const username = userInfo.username.trim();
const password = userInfo.password;
const code = userInfo.code;
const uuid = userInfo.uuid;
return new Promise((resolve, reject) => {
login(username, password, code, uuid)
.then((res) => {
setToken(res["client-token"]);
this.token = res["client-token"];
resolve();
})
})
},
// 获取用户信息
getInfo() {
return new Promise((resolve, reject) => {
getInfo().then(res => {
const user = res.user
const avatar = (user.avatar == "" || user.avatar == null) ? defAva : import.meta.env.VITE_APP_BASE_API + user.avatar;
.catch((error) => {
reject(error);
});
});
},
// 获取用户信息
getInfo() {
return new Promise((resolve, reject) => {
getInfo()
.then((res) => {
const user = res.data.user;
const avatar =
user.avatar == "" || user.avatar == null
? defAva
: import.meta.env.VITE_APP_BASE_API + user.avatar;
if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
this.roles = res.roles
this.permissions = res.permissions
if (res.roles && res.roles.length > 0) {
// 验证返回的roles是否是一个非空数组
this.roles = res.roles;
this.permissions = res.permissions;
} else {
this.roles = ['ROLE_DEFAULT']
this.roles = ["ROLE_DEFAULT"];
}
this.name = user.userName
this.name = user.userName;
this.avatar = avatar;
resolve(res)
}).catch(error => {
reject(error)
resolve(res);
})
})
},
// 退出系统
logOut() {
return new Promise((resolve, reject) => {
logout(this.token).then(() => {
this.token = ''
this.roles = []
this.permissions = []
removeToken()
resolve()
}).catch(error => {
reject(error)
.catch((error) => {
reject(error);
});
});
},
// 退出系统
logOut() {
return new Promise((resolve, reject) => {
logout(this.token)
.then(() => {
this.token = "";
this.roles = [];
this.permissions = [];
removeToken();
resolve();
})
})
}
}
})
.catch((error) => {
reject(error);
});
});
},
export default useUserStore
// 切换角色
switchRole(roleId) {
// return new Promise((resolve, reject) => {
this.roleId = roleId;
localStorage.setItem("role-id", roleId);
// });
},
},
});
export default useUserStore;

69
src/utils/parameter.js Normal file
View File

@ -0,0 +1,69 @@
// 归属导航
export const modeOptions = [
{
value: 101,
label: '中小企业服务',
},
{
value: 102,
label: '大型企业服务',
},
{
value: 103,
label: '政府企业服务',
},
{
value: 104,
label: '科研院所服务',
},
]
// 学历
export const educationOptions = [
{ key: 1, text: '小学' },
{ key: 2, text: '初中' },
{ key: 3, text: '高中' },
{ key: 4, text: '大专' },
{ key: 5, text: '本科' },
{ key: 6, text: '研究生' },
{ key: 7, text: '博士' },
]
// 企业类型
export const enterpriseOptions = [
{ key: "101", value: '上市企业' },
{ key: "102", value: '优质企业' },
{ key: "103", value: '普通企业' },
]
// 成果成熟度 技术
export const maturityOptions = [
{ key: 1, value: '正在研发' },
{ key: 2, value: '小试阶段' },
{ key: 3, value: '通过小试' },
{ key: 4, value: '中试阶段' },
{ key: 5, value: '通过中试' },
{ key: 6, value: '可规模生产' },
]
// 成果领先型 领先标准
export const leadOptions = [
{ key: 1, value: '国内先进' },
{ key: 2, value: '国内领先' },
{ key: 3, value: '国际先进' },
{ key: 4, value: '国际领先' },
]
// 专利类型
export const patentOptions = [
{ key: 1, value: '发明专利' },
{ key: 2, value: '外观设计' },
{ key: 3, value: '实用新型' },
]
// 合作模式
export const cooperationOptions = [
{ key: 101, value: '技术转让' },
{ key: 102, value: '技术许可' },
{ key: 103, value: '技术入股' },
{ key: 104, value: '合作开发' },
{ key: 105, value: '融资' },
{ key: 106, value: '公司' },
{ key: 107, value: '代理加盟' },
{ key: 108, value: '市场推广' },
{ key: 109, value: '其他' },
]

View File

@ -27,7 +27,7 @@ service.interceptors.request.use(config => {
// 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
config.headers['client-token'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
// get请求映射params参数
if (config.method === 'get' && config.params) {

View File

@ -0,0 +1,141 @@
<template>
<div class="app-container">
<el-card shadow="always" style="width: 55%; margin: 0 auto">
<p><b>基本资料</b></p>
<el-form
ref="userInfoFormRef"
:model="userInfoForm"
:rules="rules"
label-width="100px"
>
<el-form-item label="姓名:" prop="nickName">
<el-input v-model="userInfoForm.nickName" placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="手机:" prop="mobile">
<el-input v-model="userInfoForm.mobile" placeholder="请输入手机号" />
</el-form-item>
<el-form-item label="邮箱:" prop="email">
<el-input v-model="userInfoForm.email" placeholder="请输入邮箱" />
</el-form-item>
<el-form-item label="职务:" prop="post">
<el-input v-model="userInfoForm.post" placeholder="请输入职务" />
</el-form-item>
<el-form-item label="固定电话:" prop="phone">
<el-input v-model="userInfoForm.phone" placeholder="请输入固定电话" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitUserInfoForm">提交</el-button>
</el-form-item>
</el-form>
<p><b>企业资料</b></p>
<EnterpriseForm
v-model="enterpriseInfoForm"
:isAdd="false"
:labelWidth="labelWidth"
ref="enterpriseInfoFormRef"
/>
<div :style="{ marginLeft: labelWidth + 'px' }">
<el-button type="primary" @click="submitEnterpriseForm">提交</el-button>
</div>
</el-card>
</div>
</template>
<script setup>
import { expert } from "@/api/identity/index";
import { getInfo } from "@/api/login";
import { insertClientUser, updateEnterprise } from "@/api/enterprise";
import EnterpriseForm from "@/views/components/EnterpriseForm";
import { ElMessage } from "element-plus";
const data = reactive({
enterpriseInfoForm: {},
userInfoForm: {},
formData: {},
queryParams: {
pageNum: 1,
pageSize: 10,
postCode: undefined,
},
rules: {
nickName: [{ required: true, message: "姓名不能为空", trigger: "blur" }],
phone: [{ required: true, message: "电话号码不能为空", trigger: "blur" }],
mobile: [{ required: true, message: "手机号码不能为空", trigger: "blur" }],
email: [{ required: true, message: "电子邮箱不能为空", trigger: "blur" }],
post: [{ required: true, message: "职务不能为空", trigger: "blur" }],
bank: [{ required: true, message: "开户行不能为空", trigger: "blur" }],
bankAccount: [
{ required: true, message: "开户行账号不能为空", trigger: "blur" },
],
bankPhone: [
{ required: true, message: "开户行电话不能为空", trigger: "blur" },
],
address: [{ required: true, message: "地址不能为空", trigger: "blur" }],
username: [{ required: true, message: "联系人不能为空", trigger: "blur" }],
userPhone: [
{ required: true, message: "联系人电话不能为空", trigger: "blur" },
],
userAddress: [
{ required: true, message: "邮寄地址不能为空", trigger: "blur" },
],
},
});
const { queryParams, formData, rules, enterpriseInfoForm, userInfoForm } =
toRefs(data);
const { proxy } = getCurrentInstance();
const labelWidth = 140;
const form = reactive({
laboratory_id: undefined, // 所属实验室
gender: 1,
});
const getBasicInfo = async () => {
const { data } = await getInfo();
// formData.value = data.enterprise;
userInfoForm.value = data.user;
enterpriseInfoForm.value = data.enterprise;
};
// 更新个人信息
const userInfoFormRef = ref();
const submitUserInfoForm = async () => {
await userInfoFormRef.value.validate();
try {
await insertClientUser(userInfoForm.value);
getBasicInfo();
ElMessage.success("更新基本信息成功");
} catch (error) {
ElMessage.error("更新基本信息失败");
}
};
// 更新企业信息
const enterpriseInfoFormRef = ref();
const submitEnterpriseForm = async () => {
const valid = await enterpriseInfoFormRef.value.validateForm();
if (valid) {
try {
await updateEnterprise(enterpriseInfoForm.value);
getBasicInfo();
ElMessage.success("更新企业信息成功");
} catch (error) {
ElMessage.error("更新企业信息失败");
}
} else {
console.log("校验未通过");
}
};
// function submitForm(status) {
// if (proxy.$refs.expertForm.submitForm()) {
// expert(form).then((res) => {
// proxy.$modal.msgSuccess("新增成功");
// proxy.$router.go(-1);
// });
// } else {
// console.log("校验未通过");
// }
// }
getBasicInfo();
</script>

View File

@ -0,0 +1,266 @@
<template>
<el-form
ref="formRef"
:model="modelValue"
:rules="rules"
:label-width="labelWidth + 'px'"
>
<div class="form_title">
<p><b>基本信息</b></p>
</div>
<el-row>
<el-col :span="24">
<el-form-item label="产品名称:" prop="title">
<el-input
v-model="modelValue.title"
placeholder="请输入产品名称"
></el-input>
</el-form-item>
</el-col>
</el-row>
<FieldOptions
v-model="modelValue"
:labelWidth="labelWidth"
ref="fieldFormRef"
/>
<InputBoxAdd
:labelWidth="labelWidth"
v-model="modelValue"
title="应用客户"
placeholder="请输入应用客户"
fieldKey="customers"
ref="customerFormRef"
/>
<el-row>
<el-col :span="24">
<el-form-item label="产品成熟度:" prop="maturity">
<el-select
v-model="modelValue.maturity"
clearable
placeholder="请选择"
>
<el-option
v-for="item in maturityOptions"
:key="item.key"
:label="item.value"
:value="item.key"
>
</el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="成果领先性:" prop="leadStandard">
<el-select
v-model="modelValue.leadStandard"
clearable
placeholder="请选择"
>
<el-option
v-for="item in leadOptions"
:key="item.key"
:label="item.value"
:value="item.key"
>
</el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- <el-row>
<el-col :span="24">
<el-form-item label="合作模式:">
<el-select
v-model="modelValue.province"
clearable
placeholder="请选择"
>
</el-select>
</el-form-item>
</el-col>
</el-row> -->
<InputBoxAdd
:labelWidth="labelWidth"
v-model="modelValue"
title="关键词"
placeholder="请输入关键词"
fieldKey="keywords"
ref="keywordsFormRef"
/>
<el-row>
<el-col :span="24">
<el-form-item label="产品简介:" prop="introduce">
<WangEditor v-model="modelValue.introduce" :min-height="300" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="产品图片:" prop="image">
<ImageUpload v-model="modelValue.image" :limit="1" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="产品视频:">
<VideoUpload
v-model="modelValue.video"
:limit="1"
:fileType="['mp4']"
/>
</el-form-item>
</el-col>
</el-row>
<p>
<b>图片材料上传</b>
</p>
<el-row>
<el-col :span="24">
<el-form-item label="证明材料:" prop="file">
<FileUpload
v-model="modelValue.file"
:limit="1"
:fileType="['doc', 'xls', 'pdf', 'jpg', 'png', 'zip']"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script setup>
// import CityOptions from "@/views/components/CityOptions";
import FieldOptions from "@/views/components/FieldOptions";
import InputBoxAdd from "@/views/components/InputBoxAdd";
import WangEditor from "@/components/WangEditor";
import { maturityOptions, leadOptions } from "@/utils/parameter";
import VideoUpload from "@/components/VideoUpload";
const props = defineProps({
modelValue: Object,
isAdd: {
type: Boolean,
default: true,
},
showTitle: {
type: Boolean,
default: false,
},
labelWidth: {
type: Number,
default: 120,
},
});
const { modelValue, isAdd, showTitle, labelWidth } = toRefs(props);
const data = reactive({
rules: {
product: [{ required: true, message: "请输入", trigger: "blur" }],
title: [{ required: true, message: "请输入", trigger: "blur" }],
kind: [{ required: true, message: "请选择", trigger: "change" }],
code: [{ required: true, message: "请输入", trigger: "blur" }],
mobile: [
{ required: true, message: "请输入", trigger: "blur" },
{
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
message: "请输入正确的手机号码",
trigger: "blur",
},
],
research_id: [{ required: true, message: "请选择", trigger: "change" }],
maturity: [{ required: true, message: "请选择", trigger: "change" }],
tenant_id: [
{
required: true,
message: "请选择",
trigger: ["blur", "change"],
},
],
school: [{ required: true, message: "请输入", trigger: "blur" }],
education: [{ required: true, message: "请选择", trigger: "change" }],
major: [{ required: true, message: "请输入", trigger: "blur" }],
job: [{ required: true, message: "请输入", trigger: "blur" }],
title: [{ required: true, message: "请输入", trigger: "blur" }],
work_at: [
{
required: true,
message: "从业时间不能为空",
trigger: ["change", "blur"],
},
],
leadStandard: [
{
required: true,
message: "请上传",
trigger: ["blur", "change"],
},
],
introduce: [
{
required: true,
message: "请输入",
trigger: ["change", "blur"],
},
],
// image: [
// {
// required: true,
// message: "请上传",
// trigger: ["change", "blur"],
// },
// ],
// video: [
// {
// required: true,
// message: "请上传",
// trigger: ["change", "blur"],
// },
// ],
},
});
const { rules } = toRefs(data);
const formRef = ref();
const keywordsFormRef = ref();
const fieldFormRef = ref();
const customerFormRef = ref();
const validateForm = async () => {
let formValid = ref();
try {
formValid = await formRef.value.validate();
} catch (error) {
formValid = false;
}
const customerFormValid = await customerFormRef.value.validateForm(); // 领域选择表单验证
const fieldFormValid = await fieldFormRef.value.validateForm(); // 领域选择表单验证
const keywordsFormValid = await keywordsFormRef.value.validateForm(); // 关键词表单验证
return formValid && fieldFormValid && keywordsFormValid && customerFormValid;
};
defineExpose({
validateForm,
});
</script>
<style lang="scss" scoped>
.form_title {
font-weight: 700;
margin-bottom: 30px;
}
// 上传图片框限制
// ::v-deep .el-upload--picture-card {
// width: 120px;
// height: 120px;
// line-height: 120px;
// }
.el-select,
.el-date-editor {
display: block;
width: 100%;
}
</style>

View File

@ -0,0 +1,178 @@
<template>
<div class="app-container">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<router-link to="./release">
<el-button type="primary" size="mini">发布需求</el-button>
</router-link>
</el-col>
<right-toolbar
v-model:showSearch="showSearch"
@queryTable="getList"
></right-toolbar>
</el-row>
<el-radio-group
v-model="queryParams.type"
size="mini"
@change="handleQuery"
>
<el-radio-button :label="1">待受理</el-radio-button>
<el-radio-button :label="2">已受理</el-radio-button>
<el-radio-button :label="3">已结束</el-radio-button>
</el-radio-group>
<el-table v-loading="loading" :data="postList" style="margin-top: 20px">
<el-table-column label="需求名称" align="center" prop="postCode" />
<el-table-column label="需求类别" align="center" prop="postSort" />
<el-table-column label="状态" align="center" prop="status" />
<el-table-column label="联系人" align="center" prop="status" />
<el-table-column label="手机号" align="center" prop="status" />
<el-table-column
label="发布时间"
align="center"
prop="createTime"
width="180"
>
<template #default="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column
label="操作"
align="center"
class-name="small-padding fixed-width"
>
<template #default="scope">
<el-button
v-if="queryParams.type == 3"
size="mini"
type="text"
icon="Delete"
@click="handleDelete(scope.row.id)"
>删除</el-button
>
<el-button
v-if="queryParams.type == 1"
size="mini"
type="text"
icon="Close"
@click="handleDelete(scope.row.id)"
>取消发布</el-button
>
<el-button
v-if="queryParams.type != 3"
size="mini"
type="text"
icon="View"
@click="handleEdit(scope.row.id)"
>查看</el-button
>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</div>
</template>
<script setup>
import {
demand,
demandAdd,
demandEdit,
demandDetail,
demandDelete,
} from "@/api/admin/expert/demand";
const { proxy } = getCurrentInstance();
const router = useRouter();
const postList = ref([]);
const loading = ref(true);
const showSearch = ref(true);
const total = ref(0);
const dateRange = ref([]);
const data = reactive({
form: {},
queryParams: {
page_num: 1,
page_size: 10,
type: 1,
},
rules: {
name: [{ required: true, message: "公司名称不能为空", trigger: "blur" }],
dutyParagraph: [
{ required: true, message: "公司税号不能为空", trigger: "blur" },
],
bank: [{ required: true, message: "开户行不能为空", trigger: "blur" }],
bankAccount: [
{ required: true, message: "开户行账号不能为空", trigger: "blur" },
],
bankPhone: [
{ required: true, message: "开户行电话不能为空", trigger: "blur" },
],
email: [{ required: true, message: "电子邮箱不能为空", trigger: "blur" }],
phone: [{ required: true, message: "联系电话不能为空", trigger: "blur" }],
address: [{ required: true, message: "地址不能为空", trigger: "blur" }],
username: [{ required: true, message: "联系人不能为空", trigger: "blur" }],
userPhone: [
{ required: true, message: "联系人电话不能为空", trigger: "blur" },
],
userAddress: [
{ required: true, message: "邮寄地址不能为空", trigger: "blur" },
],
},
});
const { queryParams, form, rules } = toRefs(data);
/** 查询列表 */
function getList() {
loading.value = true;
postList.value = [1];
total.value = 15;
loading.value = false;
// demand(queryParams.value).then((response) => {
// console.log(response);
// // postList.value = response.data.data;
// // total.value = response.data.count;
// loading.value = false;
// });
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
function resetQuery() {
dateRange.value = [];
proxy.resetForm("queryRef");
handleQuery();
}
/** 删除按钮操作 */
function handleDelete(id) {
proxy.$modal
.confirm('是否确认删除订单号为"' + id + '"的数据项?')
.then(function () {
return demandDelete(postIds);
})
.then(() => {
getList();
proxy.$modal.msgSuccess("删除成功");
})
.catch(() => {});
}
function handleEdit(id) {
router.push({ path: "./release", query: { id } });
}
getList();
</script>

View File

@ -0,0 +1,173 @@
<template>
<div class="app-container">
<div v-loading="loading">
<div class="item" v-for="item in dataList" :key="item.id">
<div style="margin: 10px 0 10px 0">浏览时间2022-02-02 22:00:00</div>
<el-row type="flex" style="justify-content: space-between">
<div style="display: flex">
<div class="img">
<img :src="item.image" alt />
</div>
<div class="content">
<div class="tit" @click="handleDetail(item.id)">
<div class="text" style="flex: 1">{{ item.name }}</div>
</div>
<div class="line">
企业规模
<span>{{ item.kind_title || "后台暂没提供" }}</span>
</div>
<div class="line">
核心产品及应用场景 <span>{{ item.product }}</span>
</div>
<div class="line">
企业网站
<a :href="item.url"
><span>{{ item.url || "后台暂没提供" }}</span></a
>
</div>
</div>
</div>
<div class="keywords">
<wordcloud :data="createdData(item.keywords)"></wordcloud>
</div>
</el-row>
<el-divider border-style="dashed"></el-divider>
</div>
</div>
</div>
</template>
<script setup>
import wordcloud from "@/views/admin/components/wordcloud.vue";
const dataList = ref([]);
const loading = ref(true);
const total = ref(0);
const data = reactive({
queryParams: {
pageNum: 1,
pageSize: 10,
postCode: undefined,
type: "1",
},
});
const { queryParams } = toRefs(data);
const router = useRouter();
/** 查询列表 */
function getList() {
loading.value = true;
dataList.value = [
{
id: "E8MnaAkwDj1X",
kind: 103,
name: "熊",
image:
"http://192.168.0.149:8000/upload/20220221/5b3fe85e89eea53535783ccbd7905c80.jpg",
product: "核心成果核心产品",
url: "",
industrys: ["农、林、牧、渔业/农业/测试名称", "采矿业/煤炭开采和洗选业"],
keywords: ["关键词1", "关键词2"],
},
{
id: "6bPKVW8aZzXo",
kind: 103,
name: "单位名称",
image:
"http://192.168.0.149:8000/upload/20220222/4bad3f051b8f08d651ff8835fb785e46.jpg",
product: "核心成果核心产品",
url: "",
industrys: [
"农、林、牧、渔业/农业/测试名称",
"采矿业/石油和天然气开采业",
],
keywords: ["关键词1", "关键词2"],
},
];
total.value = 15;
loading.value = false;
// loading.value = true;
// listPost(queryParams.value).then(
// (response) => {
// postList.value = response.rows;
// total.value = response.total;
// loading.value = false;
// }
// );
}
function createdData(arr) {
let l = [];
let snap = JSON.parse(JSON.stringify(arr));
snap.map((e) => {
l.push({ name: e, value: 30 });
return { name: e, value: 30 };
});
return l;
}
function handleDetail(id) {
let routeData = router.resolve({
path: `/searchList/0/detail/${id}`,
query: { keyword: null },
});
window.open(routeData.href, "_blank");
}
getList();
</script>
<style lang="scss" scoped>
.item {
overflow: hidden;
background-color: #fff;
// margin-bottom: 16px;
.img {
width: 90px;
margin-right: 12px;
display: flex;
align-items: center;
justify-content: center;
img {
width: 90px;
height: 90px;
border-radius: 50%;
}
}
.content {
display: inline-block;
width: 390px;
.line {
font-size: 16px;
font-family: Source Han Sans CN;
font-weight: 400;
color: #666666;
margin: 10px 0;
span {
font-size: 16px;
font-family: Source Han Sans CN;
font-weight: 500;
color: #333;
}
}
}
// .keywords {
// flex: 1;
// width: 129px;
// height: 129px;
// }
.tit {
width: 100%;
overflow: hidden;
cursor: pointer;
.text {
margin-right: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 20px;
font-family: Source Han Sans CN;
font-weight: bold;
color: #333333;
}
}
}
</style>

View File

@ -0,0 +1,201 @@
<template>
<div class="app-container">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<router-link to="./release">
<el-button type="primary" size="small">发布产品</el-button>
</router-link>
</el-col>
</el-row>
<el-radio-group
v-model="queryParams.status"
size="small"
@change="handleQuery"
>
<el-radio-button label="1">已发布</el-radio-button>
<el-radio-button label="0">待审核</el-radio-button>
<el-radio-button label="2">已驳回</el-radio-button>
<el-radio-button label="3">草稿箱</el-radio-button>
</el-radio-group>
<el-table v-loading="loading" :data="productList" style="margin-top: 20px">
<el-table-column label="数据编号" prop="id" />
<el-table-column label="产品名称" prop="title" />
<el-table-column label="产品领域" prop="industryStr" />
<!-- <el-table-column label="浏览量" prop="visit_count" /> -->
<el-table-column label="发布时间" prop="createTime" width="180">
<template #default="{ row }">
<span>{{ row.createTime }}</span>
</template>
</el-table-column>
<el-table-column label="操作" class-name="small-padding fixed-width">
<template #default="{ row }">
<router-link
:to="{ path: './release', query: { id: row.id } }"
v-if="queryParams.status == 0"
>
<el-button size="small" type="text" icon="Edit">编辑</el-button>
</router-link>
<el-button
v-if="queryParams.status == 2"
size="small"
type="text"
icon="Download"
@click="handleShelf(row)"
>下架</el-button
>
<el-button
v-if="queryParams.status != 1"
size="small"
type="text"
icon="Delete"
@click="handleDelete(row.id)"
>删除</el-button
>
<el-button
v-if="queryParams.status == 2"
size="small"
type="text"
icon="View"
@click="handleResults(row.id)"
>查看匹配结果</el-button
>
<el-button
v-if="queryParams.status == 2"
size="small"
type="text"
icon="View"
@click="handlePages(row.id)"
>浏览企业信息</el-button
>
<el-button
v-if="queryParams.status == 1"
size="small"
type="text"
icon="Close"
>取消发布</el-button
>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</div>
</template>
<script setup name="Product">
// import {
// expertAchievement,
// achievementShelf,
// achievementDelete,
// } from "@/api/admin/expert/technology";
import { getEnterpriseProduct } from "@/api/enterprise";
import { useRouter } from "vue-router";
const { proxy } = getCurrentInstance();
const router = useRouter();
const productList = ref([]);
const loading = ref(true);
const showSearch = ref(true);
const total = ref(0);
const dateRange = ref([]);
const data = reactive({
form: {},
queryParams: {
pageNum: 1,
pageSize: 10,
status: "0",
},
rules: {
name: [{ required: true, message: "公司名称不能为空", trigger: "blur" }],
dutyParagraph: [
{ required: true, message: "公司税号不能为空", trigger: "blur" },
],
bank: [{ required: true, message: "开户行不能为空", trigger: "blur" }],
bankAccount: [
{ required: true, message: "开户行账号不能为空", trigger: "blur" },
],
bankPhone: [
{ required: true, message: "开户行电话不能为空", trigger: "blur" },
],
email: [{ required: true, message: "电子邮箱不能为空", trigger: "blur" }],
phone: [{ required: true, message: "联系电话不能为空", trigger: "blur" }],
address: [{ required: true, message: "地址不能为空", trigger: "blur" }],
username: [{ required: true, message: "联系人不能为空", trigger: "blur" }],
userPhone: [
{ required: true, message: "联系人电话不能为空", trigger: "blur" },
],
userAddress: [
{ required: true, message: "邮寄地址不能为空", trigger: "blur" },
],
},
});
const { queryParams, form, rules } = toRefs(data);
/** 查询列表 */
const getList = async () => {
loading.value = true;
const resp = await getEnterpriseProduct(queryParams.value);
productList.value = resp.rows;
total.value = resp.total;
loading.value = false;
};
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
function resetQuery() {
dateRange.value = [];
proxy.resetForm("queryRef");
handleQuery();
}
/** 删除按钮操作 */
function handleDelete(id) {
proxy.$modal
.confirm('是否确认删除数据编号为"' + id + '"的产品项?')
.then(function () {
return achievementDelete({ id });
})
.then(() => {
getList();
proxy.$modal.msgSuccess("删除成功");
})
.catch(() => {});
}
function handleShelf(row) {
let text = row.shelf_status == 2 ? "上架" : "下架";
proxy.$modal
.confirm('确认要"' + text + '""' + row.id + '"的产品吗?')
.then(function () {
let status = row.shelf_status == 1 ? 2 : 1;
return achievementShelf({ id: row.id, status });
})
.then(() => {
getList();
proxy.$modal.msgSuccess(text + "成功");
})
.catch(() => {});
}
function handleResults(id) {
router.push({ path: "./results" });
}
function handlePages(id) {
router.push({ path: "./business" });
}
// console.log(route);
getList();
</script>

View File

@ -0,0 +1,47 @@
<template>
<div class="app-container">
<el-card shadow="always" style="width: 55%; margin: 0 auto">
<ReleaseForm
v-model="form"
:isAdd="false"
:labelWidth="labelWidth"
ref="releaseFormRef"
/>
<div :style="{ marginLeft: labelWidth + 'px' }">
<el-button type="primary" @click="submitForm('0')">保存草稿</el-button>
<el-button type="primary" @click="submitForm('1')">提交审核</el-button>
</div>
</el-card>
</div>
</template>
<script setup>
import { insertEnterpriseProduct } from "@/api/enterprise";
import { ElMessage } from "element-plus";
import { useRoute, useRouter } from "vue-router";
import ReleaseForm from "../components/ReleaseForm";
import { getProductById } from "@/api/enterprise";
const route = useRoute();
const labelWidth = 140;
const form = reactive({});
const router = useRouter();
const releaseFormRef = ref();
const submitForm = async (status) => {
form.status = status;
const valid = await releaseFormRef.value.validateForm();
if (valid) {
await insertEnterpriseProduct(form);
ElMessage.success("新增产品成功");
router.go(-1);
} else {
console.log("校验未通过");
}
};
if (route.query.id) {
getProductById({ id: route.query.id }).then((resp) => {
console.log(resp);
});
// console.log(route.query.id);
// queryParams.value.id = route.query.id;
}
</script>

View File

@ -0,0 +1,222 @@
<template>
<div class="app-container">
<el-radio-group
v-model="queryParams.type"
size="mini"
@change="handleQuery"
>
<el-radio-button label="1">成果</el-radio-button>
<el-radio-button label="2">专利</el-radio-button>
<el-radio-button label="3">专家</el-radio-button>
<el-radio-button label="4">实验室</el-radio-button>
<el-radio-button label="5">企业</el-radio-button>
</el-radio-group>
<!-- <el-row :gutter="20">
<el-col
:xs="12"
:sm="6"
:lg="4"
style="margin-top: 20px"
v-for="item in 10"
:key="item"
>
<div style="border: 1px solid #787878">
<div style="text-align: right">
<el-tag type="warning" effect="dark" style="border-radius: 0">
7.8万
</el-tag>
</div>
<div style="padding: 10px">
<div class="ellipsis">
高碳当量高强度灰铁材质研发高碳当量高强度灰铁材质研发
</div>
<div
style="
margin: 10px 0;
display: flex;
justify-content: space-between;
"
>
<el-tag type="info" effect="plain">所属领域</el-tag>
<el-tag type="info" effect="plain">普通企业需求</el-tag>
</div>
<div style="margin-bottom: 10px">研发类型:</div>
<div>截止时间:</div>
</div>
<div class="btn_info pointer">查看详情</div>
</div>
</el-col>
</el-row> -->
<div style="margin-top: 20px" v-loading="loading">
<section v-for="item in postList" :key="item.id">
<div style="border: 1px solid #dcdcdc; margin-bottom: 10px">
<gainItem :data="item"></gainItem>
</div>
</section>
</div>
<!-- <pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/> -->
</div>
</template>
<script setup name="Post">
import {
listPost,
addPost,
delPost,
getPost,
updatePost,
} from "@/api/system/post";
import gainItem from "@/views/admin/components/gainItem.vue";
const { proxy } = getCurrentInstance();
const router = useRouter();
const postList = ref([]);
const loading = ref(true);
const total = ref(0);
const data = reactive({
form: {},
queryParams: {
pageNum: 1,
pageSize: 10,
postCode: undefined,
type: "1",
},
rules: {
name: [{ required: true, message: "公司名称不能为空", trigger: "blur" }],
dutyParagraph: [
{ required: true, message: "公司税号不能为空", trigger: "blur" },
],
bank: [{ required: true, message: "开户行不能为空", trigger: "blur" }],
bankAccount: [
{ required: true, message: "开户行账号不能为空", trigger: "blur" },
],
bankPhone: [
{ required: true, message: "开户行电话不能为空", trigger: "blur" },
],
email: [{ required: true, message: "电子邮箱不能为空", trigger: "blur" }],
phone: [{ required: true, message: "联系电话不能为空", trigger: "blur" }],
address: [{ required: true, message: "地址不能为空", trigger: "blur" }],
username: [{ required: true, message: "联系人不能为空", trigger: "blur" }],
userPhone: [
{ required: true, message: "联系人电话不能为空", trigger: "blur" },
],
userAddress: [
{ required: true, message: "邮寄地址不能为空", trigger: "blur" },
],
},
});
const { queryParams, form, rules } = toRefs(data);
/** 查询列表 */
function getList() {
postList.value = [
{
is_collect: true,
id: "Q7K1Jx6VYodq",
mode: 1,
title: "测试6",
description: "",
image: "",
file: "",
maturity: 1,
lead_standard: 0,
cooperation_mode: 101,
config: "",
introduce: "",
shelf_status: 0,
status: 0,
created_at: "0001-01-01T00:00:00Z",
updated_at: "0001-01-01T00:00:00Z",
research_name: "",
visit_count: 0,
collect_count: 0,
mode_title: "免费模式",
maturity_title: "正在研发",
cooperation_mode_title: "技术转让",
customers: ["关键词1", "关键词2"],
industrys: ["农、林、牧、渔业/农业", "采矿业/煤炭开采和洗选业", "制造业"],
keywords: ["关键词1", "关键词2"],
},
{
is_collect: false,
id: "gympJE5JrLxe",
mode: 1,
title: "测试5",
description: "",
image: "",
file: "",
maturity: 1,
lead_standard: 0,
cooperation_mode: 101,
config: "",
introduce: "",
shelf_status: 0,
status: 0,
created_at: "0001-01-01T00:00:00Z",
updated_at: "0001-01-01T00:00:00Z",
research_name: "",
visit_count: 0,
collect_count: 0,
mode_title: "免费模式",
maturity_title: "正在研发",
cooperation_mode_title: "技术转让",
customers: ["关键词1", "关键词2"],
industrys: ["农、林、牧、渔业/农业", "采矿业/煤炭开采和洗选业", "制造业"],
keywords: ["关键词1", "关键词2"],
},
];
total.value = 15;
loading.value = false;
// loading.value = true;
// listPost(queryParams.value).then(
// (response) => {
// postList.value = response.rows;
// total.value = response.total;
// loading.value = false;
// }
// );
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1;
getList();
}
/** 删除按钮操作 */
function handleDelete(id) {
proxy.$modal
.confirm('是否确认删除订单号为"' + id + '"的数据项?')
.then(function () {
return delPost(postIds);
})
.then(() => {
getList();
proxy.$modal.msgSuccess("删除成功");
})
.catch(() => {});
}
function handleResults(id) {
router.push({ path: "./results" });
}
getList();
</script>
<style lang="scss" scoped>
.btn_info {
height: 35px;
line-height: 35px;
text-align: center;
color: #fff;
background-color: #787878;
}
</style>

93
src/views/admin/index.vue Normal file
View File

@ -0,0 +1,93 @@
<template>
<div class="app-container">
<div class="card-panel surplus-currency" :class="flag ? 'vip-box' : ''">
<div class="_tit">
<span> 会员banner图 </span>
<div class="fr" v-if="flag">
<div class="text-right">续期</div>
<div>升级VIP</div>
</div>
<div class="text-right" style="margin-top: 50px" v-if="flag">
会员剩余天数89天
</div>
</div>
<span class="pointer" style="font-size: 14px" v-if="flag">
查看会员权益
</span>
<p class="text-center pointer" v-else>开通VIP</p>
</div>
<div class="card-panel surplus-currency">
<span style="margin-right: 50px; font-size: 16px; font-weight: 700"
>剩余创新币98</span
>
<el-button class="x_btns">创新币充值</el-button>
</div>
<el-card style="margin-top: 20px">
<template #header>
<div>快捷功能</div>
</template>
<el-row :gutter="20">
<el-col :span="3">
<router-link to="/extension/release">
<el-card shadow="never" class="text-center pointer">
发布产品
</el-card>
</router-link>
</el-col>
<el-col :span="21">
<el-card shadow="never" class="text-center">文字简介</el-card>
</el-col>
</el-row>
<el-row :gutter="20" class="mt20">
<el-col :span="3">
<router-link to="/demand/released">
<el-card shadow="never" class="text-center pointer">
发布需求
</el-card>
</router-link>
</el-col>
<el-col :span="21">
<el-card shadow="never" class="text-center">技术需求</el-card>
</el-col>
</el-row>
</el-card>
</div>
</template>
<script setup>
import { ref } from "vue";
const flag = ref(true);
</script>
<style lang="scss" scoped>
:deep(el-card__header) {
padding-top: 0;
padding-bottom: 0;
div {
height: 40px;
line-height: 40px;
}
}
.surplus-currency {
margin-bottom: 20px;
height: 150px;
background-color: #f2f2f2;
padding: 20px 50px;
._tit {
font-size: 24px;
font-weight: 700;
color: #bebebe;
div {
font-size: 18px;
color: #000;
}
}
}
.vip-box {
height: auto;
background-color: yellow;
}
</style>

View File

@ -0,0 +1,174 @@
<template>
<el-form
ref="formRef"
:model="modelValue"
:rules="rules"
:label-width="`${labelWidth}px`"
>
<el-row>
<el-col :span="24">
<el-form-item label="所在地:" required>
<el-row type="flex" justify="space-between">
<el-col :span="7">
<el-form-item prop="province">
<el-select
v-model="modelValue.province"
clearable
filterable
placeholder="请选择"
:disabled="provinceSelectList.length === 0"
@change="provinceChanged"
>
<el-option
v-for="item in provinceSelectList"
:key="item.provinceCode"
:label="item.provinceName"
:value="item.provinceCode"
>
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="7">
<el-form-item prop="city">
<el-select
v-model="modelValue.city"
clearable
filterable
:disabled="citySelectList.length === 0"
placeholder="请选择"
@change="cityChanged"
>
<el-option
v-for="item in citySelectList"
:key="item.cityCode"
:label="item.cityName"
:value="item.cityCode"
>
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="7">
<el-form-item prop="district">
<el-select
v-model="modelValue.district"
clearable
filterable
:disabled="districtSelectList.length === 0"
placeholder="请选择"
>
<el-option
v-for="item in districtSelectList"
:key="item.areaCode"
:label="item.areaName"
:value="item.areaCode"
>
</el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script setup name="CityOptions">
import { provinceList, cityList, districtList } from "@/api/config";
import { reactive, ref, toRefs } from "vue";
const props = defineProps({
modelValue: Object,
labelWidth: {
type: Number,
default: 120,
},
});
const { modelValue, labelWidth } = toRefs(props);
const formRef = ref(null);
const provinceSelectList = ref([]); // 省
const citySelectList = ref([]); // 市
const districtSelectList = ref([]); // 区\
const data = reactive({
rules: {
province: [
{ required: true, message: "请选择", trigger: ["change", "blue"] },
],
city: [{ required: true, message: "请选择", trigger: ["change", "blue"] }],
district: [
{ required: true, message: "请选择", trigger: ["change", "blue"] },
],
},
});
const { rules } = toRefs(data);
// 获取 省列表
const getProvinceList = async () => {
const resp = await provinceList();
provinceSelectList.value = resp.data.map((el) => {
return { ...el, provinceCode: el.provinceCode.toString() };
});
};
// 获取市列表
const getCityListByProvinceId = async (provinceId) => {
const { data } = await cityList(provinceId);
citySelectList.value = data.map((el) => {
return {
...el,
cityCode: el.cityCode.toString(),
};
});
};
// 根据市id获取县区列表
const getAreaListByCityId = async (cityId) => {
const { data } = await districtList(cityId);
districtSelectList.value = data.map((el) => {
return {
...el,
areaCode: el.areaCode.toString(),
};
});
};
// 当省改变时
const provinceChanged = () => {
// 清除市县代码列表
modelValue.value.city = undefined;
modelValue.value.district = undefined;
// 清除市县列表
citySelectList.value = [];
districtSelectList.value = [];
// 重新请求城市列表
modelValue.value.province &&
getCityListByProvinceId(modelValue.value.province);
};
// 当市改变时
const cityChanged = () => {
// 清除县区代码列表
modelValue.value.district = undefined;
districtSelectList.value = [];
modelValue.value.city && getAreaListByCityId(modelValue.value.city);
};
const validateForm = async () => {
try {
return await formRef.value.validate();
} catch (error) {
return false;
}
};
watch(modelValue, (val) => {
if (val.province) {
getCityListByProvinceId(val.province);
}
if (val.city) {
getAreaListByCityId(val.city);
}
});
getProvinceList();
defineExpose({
validateForm,
});
</script>

View File

@ -0,0 +1,263 @@
<template>
<el-form
ref="formRef"
:model="modelValue"
:rules="rules"
:label-width="labelWidth + 'px'"
>
<div class="form_title" v-if="showTitle">基本信息</div>
<el-row v-if="isAdd">
<el-col :span="24">
<el-form-item label="企业logo:">
<ImageUpload v-model="modelValue.image" :limit="1" />
</el-form-item>
</el-col>
</el-row>
<el-row v-if="isAdd">
<el-col :span="24">
<el-form-item label="单位名称" prop="name">
<el-input v-model="modelValue.name"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row v-if="isAdd">
<el-col :span="24">
<el-form-item label="组织机构代码:" prop="code">
<el-row type="flex" justify="space-between">
<el-col :span="20">
<el-input v-model="modelValue.code"></el-input>
</el-col>
<el-col :span="3">
<el-button type="primary" @click="">查找</el-button>
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="企业类型:" prop="kind">
<el-select v-model="modelValue.kind" placeholder="请选择">
<el-option
v-for="item in enterpriseOptions"
:key="item.key"
:label="item.value"
:value="item.key"
>
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="核心成果核心产品:" prop="product">
<el-input v-model="modelValue.product"></el-input>
</el-form-item>
</el-col>
</el-row>
<CityOptions
v-model="modelValue"
:labelWidth="labelWidth"
ref="cityFormRef"
/>
<el-row>
<el-col :span="24">
<el-form-item label="详细地址:">
<el-input v-model="modelValue.address"></el-input>
</el-form-item>
</el-col>
</el-row>
<FieldOptions
v-model="modelValue"
:labelWidth="labelWidth"
ref="fieldFormRef"
/>
<InputBoxAdd
:labelWidth="labelWidth"
v-model="modelValue"
title="关键词"
placeholder="应用场景关键词+技术产品关键词"
fieldKey="keywords"
ref="keywordsFormRef"
/>
<InputBoxAdd
v-if="isAdd"
:labelWidth="labelWidth"
v-model="modelValue"
title="生产方向"
placeholder="请输入生产方向"
fieldKey="directions"
ref="directionsFormRef"
/>
<el-row>
<!-- <el-col :span="24">
<el-form-item label="邀请码:">
<el-input v-model="modelValue.inviter_code"></el-input>
</el-form-item>
</el-col> -->
<el-col :span="24">
<el-form-item label="企业网站:">
<el-input v-model="modelValue.url"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row v-if="isAdd">
<el-col :span="24">
<el-form-item label="营业执照:" prop="license">
<ImageUpload
v-model="modelValue.license"
:isShowTip="false"
:limit="1"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="单位简介" prop="introduce">
<WangEditor v-model="modelValue.introduce" :minHeight="300" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script setup name="EnterpriseForm">
import CityOptions from "../CityOptions";
import FieldOptions from "../FieldOptions";
import InputBoxAdd from "../InputBoxAdd";
// import { researchSelect, laboratorySelect } from "@/api/identity/index";
import WangEditor from "@/components/WangEditor";
import { enterpriseOptions } from "@/utils/parameter";
import { toRefs } from "vue";
const props = defineProps({
modelValue: Object,
isAdd: {
type: Boolean,
default: true,
},
showTitle: {
type: Boolean,
default: false,
},
labelWidth: {
type: Number,
default: 120,
},
});
const { modelValue, isAdd, showTitle, labelWidth } = toRefs(props);
const data = reactive({
rules: {
product: [{ required: true, message: "请输入", trigger: "blur" }],
name: [{ required: true, message: "请输入", trigger: "blur" }],
kind: [{ required: true, message: "请选择", trigger: "change" }],
code: [{ required: true, message: "请输入", trigger: "blur" }],
mobile: [
{ required: true, message: "请输入", trigger: "blur" },
{
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
message: "请输入正确的手机号码",
trigger: "blur",
},
],
research_id: [{ required: true, message: "请选择", trigger: "change" }],
tenant_id: [
{
required: true,
message: "请选择",
trigger: ["blur", "change"],
},
],
school: [{ required: true, message: "请输入", trigger: "blur" }],
education: [{ required: true, message: "请选择", trigger: "change" }],
major: [{ required: true, message: "请输入", trigger: "blur" }],
job: [{ required: true, message: "请输入", trigger: "blur" }],
title: [{ required: true, message: "请输入", trigger: "blur" }],
work_at: [
{
required: true,
message: "从业时间不能为空",
trigger: ["change", "blur"],
},
],
license: [
{
required: true,
message: "请上传",
trigger: ["blur", "change"],
},
],
introduce: [
{
required: true,
message: "请输入",
trigger: ["change", "blur"],
},
],
},
});
const { rules } = toRefs(data);
const formRef = ref();
const cityFormRef = ref();
const fieldFormRef = ref();
const keywordsFormRef = ref();
const directionsFormRef = ref();
const validateForm = async () => {
let formValid;
try {
formValid = await formRef.value.validate();
} catch (error) {
formValid = false;
}
const cityFormValid = await cityFormRef.value.validateForm(); // 城市选择表单验证
const fieldFormValid = await fieldFormRef.value.validateForm(); // 领域选择表单验证
const keywordsFormValid = await keywordsFormRef.value.validateForm(); // 关键词表单验证
console.log(cityFormValid);
if (isAdd.value) {
const directionsFormValid = await directionsFormRef.value.validateForm();
return (
formValid &&
cityFormValid &&
fieldFormValid &&
keywordsFormValid &&
directionsFormValid
);
} else {
return formValid && cityFormValid && fieldFormValid && keywordsFormValid;
}
};
defineExpose({
validateForm,
});
</script>
<style lang="scss" scoped>
.form_title {
font-weight: 700;
margin-bottom: 30px;
}
// 上传图片框限制
// ::v-deep .el-upload--picture-card {
// width: 120px;
// height: 120px;
// line-height: 120px;
// }
.el-select,
.el-date-editor {
display: block;
width: 100%;
}
</style>

View File

@ -0,0 +1,218 @@
<template>
<el-form
ref="formRef"
:model="modelValue"
:rules="rules"
:label-width="labelWidth + 'px'"
:disabled="disabled"
>
<el-row>
<el-col :span="24">
<el-form-item label="所属领域:" required :show-message="false">
<el-row type="flex" justify="space-between">
<el-col :span="6">
<el-form-item prop="industrys">
<el-select
v-model="fields[0]"
value-key="id"
placeholder="请选择"
@change="levelIChange"
>
<el-option
v-for="item in levelI"
:key="item.id"
:label="item.name"
:value="item.id"
>
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-select
v-model="fields[1]"
value-key="id"
placeholder="请选择"
:disabled="levelII.length === 0"
@change="levelIIChange"
>
<el-option
v-for="item in levelII"
:key="item.id"
:label="item.name"
:value="item.id"
>
</el-option>
</el-select>
</el-col>
<el-col :span="6">
<el-select
v-model="fields[2]"
value-key="id"
:disabled="levelIII.length === 0"
placeholder="请选择"
>
<el-option
v-for="item in levelIII"
:key="item.id"
:label="item.name"
:value="item.id"
>
</el-option>
</el-select>
</el-col>
<el-col :span="3">
<el-button type="primary" @click="fieldAdd">添加</el-button>
<!-- <el-button type="primary" @click="check">验证</el-button> -->
</el-col>
</el-row>
<div class="e_tag">
<el-tag
v-for="(tag, index) in industrysTags"
:key="index"
closable
@close="handleFieldClose(index)"
>
{{ getFieldNameById(tag) }}
<!-- <template v-if="Array.isArray(tag)">
<span v-for="(item, i) in tag" :key="item">
{{ item.name }}
<span v-if="tag.length != i + 1">></span>
</span>
</template>
<template v-else>
<span>{{ tag }}</span>
</template> -->
</el-tag>
</div>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script setup name="FieldOptions">
// import { industry } from "@/api/config";
import { listSysIndustry } from "@/api/config";
import { ElMessage } from "element-plus";
import { toRefs, watch } from "vue";
const props = defineProps({
modelValue: Object,
labelWidth: {
type: Number,
default: 120,
},
disabled: {
type: Boolean,
default: false,
},
});
const { modelValue, labelWidth, disabled } = toRefs(props);
const formRef = ref(null);
const levelI = ref([]); // I级数据
const levelII = ref([]); // II级数据
const levelIII = ref([]); // III级数据
const fields = ref([]); // 当前下拉框选中的集合
const industrysTags = ref([]); // 点击添加按钮后临时存储的数据集合
const data = reactive({
rules: {
industrys: [
{
type: "array",
required: true,
message: "请选择并添加",
trigger: "change",
},
],
},
});
const { rules } = toRefs(data);
// 获取领域树形列表
const getIndustryTreeData = async () => {
const { data } = await listSysIndustry();
levelI.value = data;
};
const levelIChange = async (item) => {
delete fields.value[1];
delete fields.value[2];
levelII.value = levelI.value.find((el) => {
return el.id === item;
}).children;
};
const levelIIChange = async (item) => {
delete fields.value[2];
levelIII.value = levelII.value.find((el) => el.id === item).children;
};
// 根据id获取领域名称
// TODO:如果领域已经被删除,则显示未知领域
const getFieldNameById = (ids) => {
if (levelI.value.length === 0) {
return;
}
let fieldNameList = [];
let subFieldList = [];
for (let i = 0; i < ids.length; i++) {
const id = ids[i];
if (fieldNameList.length === 0) {
subFieldList = levelI.value.find((el) => el.id == id)?.children;
fieldNameList.push(levelI.value.find((el) => id === el.id)?.name);
} else {
fieldNameList.push(subFieldList.find((el) => id === el.id)?.name);
subFieldList = subFieldList.find((el) => el.id == id)?.children;
}
}
return fieldNameList.join(">");
};
// 所属领域添加按钮
const fieldAdd = () => {
if (!fields.value.length) return ElMessage.error("请选择领域类型");
industrysTags.value.push(fields.value);
if (!modelValue.value.industrys) modelValue.value.industrys = [];
modelValue.value.industrys.push(fields.value.join("-"));
fields.value = [];
levelII.value = [];
levelIII.value = [];
};
const handleFieldClose = (index) => {
industrysTags.value.splice(index, 1);
modelValue.value.industrys.splice(index, 1);
formRef.value.validate();
};
const validateForm = async () => {
try {
return await formRef.value.validate();
} catch (error) {
return false;
}
};
getIndustryTreeData();
watch(modelValue, (newVal) => {
modelValue.value.industrys = [];
industrysTags.value = [];
modelValue.value.industrys.push(...modelValue.value.industry.split(","));
for (const field of modelValue.value.industry.split(",")) {
industrysTags.value.push(field.split("-").map((el) => parseInt(el)));
}
});
defineExpose({
validateForm,
});
</script>
<style lang="scss" scoped>
.e_tag {
.el-tag {
margin-right: 10px;
}
}
</style>

View File

@ -0,0 +1,146 @@
<template>
<el-form
ref="form"
:model="modelValue"
:rules="rules"
:label-width="labelWidth + 'px'"
>
<el-row>
<el-col :span="24">
<el-form-item label="所属领域:" required>
<el-row type="flex" justify="space-between">
<el-col :span="7">
<el-form-item prop="industrys">
<el-select
v-model="modelValue.industrys[0]"
value-key="id"
placeholder="请选择"
@change="levelIChange"
>
<el-option
v-for="item in levelI"
:key="item.id"
:label="item.name"
:value="item.id"
>
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="7">
<el-select
v-model="modelValue.industrys[1]"
value-key="id"
placeholder="请选择"
@change="levelIIChange"
>
<el-option
v-for="item in levelII"
:key="item.id"
:label="item.name"
:value="item.id"
>
</el-option>
</el-select>
</el-col>
<el-col :span="7">
<el-select
v-model="modelValue.industrys[2]"
value-key="id"
placeholder="请选择"
>
<el-option
v-for="item in levelIII"
:key="item.id"
:label="item.name"
:value="item.id"
>
</el-option>
</el-select>
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script>
import { industry } from "@/api/config";
export default {
props: {
modelValue: Object,
labelWidth: {
type: Number,
default: 120,
},
},
data() {
return {
levelI: [], // I级数据
levelII: [], // II级数据
levelIII: [], // III级数据
rules: {
industrys: [
{
type: "array",
required: true,
message: "请选择",
trigger: "change",
},
],
},
};
},
watch: {
// modelValue(newVal, oldVal) {
// let _key = [];
// let _value = [];
// for (let i = 0; i < newVal.industrys.length; i++) {
// const item = newVal.industrys[i];
// _key.push(item.key);
// _value.push(item.value);
// }
// newVal.industrys = _key;
// },
},
methods: {
getFieldByParent(id) {
return new Promise((resolve, reject) => {
industry({ parent_id: id })
.then(({ code, msg, data }) => {
if (code == 200) {
resolve(data);
} else {
this.$modal.msgError(msg);
reject({ msg, code });
}
})
.catch((error) => {
reject(error);
});
});
},
async levelIChange(id) {
delete this.modelValue.industrys[1];
delete this.modelValue.industrys[2];
this.levelII = await this.getFieldByParent(id);
},
async levelIIChange(id) {
delete this.modelValue.industrys[2];
this.levelIII = await this.getFieldByParent(id);
},
submitForm() {
let flag = false;
this.$refs["form"].validate((valid) => {
flag = valid;
});
return flag;
},
},
created() {
industry().then((res) => {
this.levelI = res.data;
});
},
};
</script>

View File

@ -0,0 +1,109 @@
<template>
<el-form
ref="formRef"
:model="modelValue"
:label-width="`${labelWidth}px`"
:disabled="disabled"
>
<el-row>
<el-col :span="24">
<el-form-item
:label="`${title}:`"
:prop="fieldKey"
:rules="[
{
required: true,
type: 'array',
message: '请输入并添加',
trigger: 'change',
},
]"
>
<el-row type="flex" justify="space-between">
<el-col :span="20">
<el-input v-model="dataVal" :placeholder="placeholder"></el-input>
</el-col>
<el-col :span="3">
<el-button type="primary" @click="keyWordAdd">添加</el-button>
</el-col>
</el-row>
<div class="e_tag">
<el-tag
v-for="(tag, index) in modelValue[fieldKey]"
:key="index"
closable
@close="handleFieldClose(fieldKey, index)"
>
{{ tag }}
</el-tag>
</div>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script setup name="InputBoxAdd">
// import { industry } from "@/api/config";
import { ElMessage } from "element-plus";
import { ref, toRefs } from "vue";
const props = defineProps({
modelValue: Object,
labelWidth: {
type: Number,
default: 120,
},
title: {
type: String,
default: "",
},
placeholder: {
type: String,
default: "",
},
fieldKey: {
require: true,
type: String,
},
disabled: {
type: Boolean,
default: false,
},
});
const { modelValue, title, fieldKey, placeholder, disabled, labelWidth } =
toRefs(props);
const dataVal = ref("");
const formRef = ref(null);
const keyWordAdd = () => {
if (!dataVal.value.length) return ElMessage.error(`请输入${title.value}`);
if (!modelValue.value[fieldKey.value]) modelValue.value[fieldKey.value] = [];
modelValue.value[fieldKey.value].push(dataVal.value);
dataVal.value = "";
};
const handleFieldClose = (val, index) => {
modelValue.value[val].splice(index, 1);
};
const validateForm = async () => {
try {
return await formRef.value.validate();
} catch (error) {
return false;
}
};
defineExpose({
validateForm,
});
</script>
<style lang="scss" scoped>
.e_tag {
width: 100%;
.el-tag {
margin-right: 10px;
}
}
</style>

View File

@ -0,0 +1,42 @@
<template>
<div class="app-container">
<el-card shadow="always" style="width: 55%; margin: 0 auto">
<EnterpriseForm
v-model="form"
:isAdd="false"
:labelWidth="labelWidth"
ref="enterpriseFormRef"
/>
<div :style="{ marginLeft: labelWidth + 'px' }">
<el-button @click="$router.go(-1)">取消</el-button>
<el-button type="primary" @click="submitForm">提交</el-button>
</div>
</el-card>
</div>
</template>
<script setup>
import { insertEnterprise } from "@/api/identity/index";
import { ElMessage } from "element-plus";
import { reactive, toRefs } from "vue";
import { useRouter } from "vue-router";
import EnterpriseForm from "../components/EnterpriseForm/index.vue";
const router = useRouter();
const labelWidth = 140;
const data = reactive({ form: {} });
const { form } = toRefs(data);
const enterpriseFormRef = ref();
const submitForm = async (status) => {
const valid = await enterpriseFormRef.value.validateForm();
if (valid) {
insertEnterprise(form.value).then(() => {
ElMessage.success("新增成功");
router.go(-1);
});
} else {
console.log("校验未通过");
}
};
</script>

View File

@ -0,0 +1,172 @@
<template>
<div class="app-container" v-if="identityList.length">
<el-card shadow="always">
<el-row :gutter="20" justify="center">
<el-col
:span="4"
v-for="item in identityList.slice(0, 5)"
:key="item.id"
@click="handleStatus(item)"
>
<el-card style="text-align: center; height: 100%">
<el-image
style="height: 100px"
src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"
fit="cover"
></el-image>
<h3>{{ `${item.title}入驻` }}</h3>
<p v-if="item.status == 0">审核中</p>
<div v-else-if="item.status == 2">
<p class="text-danger">审核拒绝</p>
<p
class="text-navy"
style="cursor: pointer"
@click.stop="reason(item)"
>
查看拒绝原因
</p>
</div>
<!-- <div v-else-if="item.status == 4">
<el-link type="primary">申请</el-link>
</div> -->
</el-card>
</el-col>
</el-row>
</el-card>
<el-card class="mt20" shadow="always">
<el-row :gutter="20" justify="center">
<el-col
:span="4"
v-for="item in identityList.slice(0, 5)"
:key="item.id"
>
<el-card style="text-align: center; height: 100%">
<el-image
style="height: 100px"
src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"
fit="cover"
></el-image>
<h3>{{ `${item.title}后台` }}</h3>
<p v-if="item.status == 1">
<el-link type="primary" @click="handlePage(item)">进入</el-link>
</p>
</el-card>
</el-col>
</el-row>
</el-card>
</div>
</template>
<script setup>
// import store from "@/store";
import { identity, settled, identitySwitch } from "@/api/identity/index";
import { useRouter } from "vue-router";
import usePermissionStore from "@/store/modules/permission";
import useUserStore from "@/store/modules/user";
const router = useRouter();
const permissionStore = usePermissionStore();
const userStore = useUserStore();
const identityList = ref([]);
const identityDict = {
1: "企业",
2: "专家",
3: "研究机构",
4: "实验室",
5: "科技经纪人",
// 6: "企业后台",
// 7: "专家后台",
// 8: "研究机构后台",
// 9: "实验室后台",
// 10: "科技经纪人后台",
};
onMounted(() => {
identity().then((res) => {
console.log(res.data);
res.data.forEach((item) => {
identityList.value.push({
id: item.roleId,
title: identityDict[item.roleId],
status: item.status,
});
});
// settled().then((ret) => {
// for (const key in res.data) {
// const obj = { id: key, title: res.data[key], status: -1, remark: "" };
// if (ret.data.examine_identity[key] !== undefined) {
// obj.status = ret.data.examine_identity[key].status;
// obj.remark = ret.data.examine_identity[key].remark;
// }
// if ((ret.data.identity & key) > 0) {
// obj.status = 1;
// }
// identityList.value.push(obj);
// }
// });
});
});
function reason(item) {
alert("拒绝原因:\n" + item.remark);
}
function noClicking() {
return identityList.value.some((item) => item.status === 0);
}
// item.status -1>未入驻 0>审核中 1>通过 2拒绝
function handleStatus(item) {
if (noClicking()) return alert("你有已入住申请中");
if (item.status === "4" || item.status === "2") {
if (item.id == 1) {
// 企业
router.push({ path: "/identity/enterprise" });
} else if (item.id == 2) {
// 专家
router.push({ path: "/identity/expert" });
} else if (item.id == 3) {
// 研究机构
router.push({ path: "/identity/research" });
} else if (item.id == 4) {
// 实验室
router.push({ path: "/identity/laboratory" });
} else if (item.id == 5) {
// 科技经纪人
router.push({ path: "/identity/agent" });
}
} else if (item.status === 1) {
alert("您已入驻,请进入后台");
} else {
}
}
const handlePage = async (item) => {
let routeData = "";
if (item.id == 1) {
// 企业
routeData = router.resolve({ path: "/admin" });
} else if (item.id == 2) {
// 专家
routeData = router.resolve({ path: "/admin" });
} else if (item.id == 3) {
// 研究机构
routeData = router.resolve({ path: "/admin" });
} else if (item.id == 4) {
// 实验室
routeData = router.resolve({ path: "/admin" });
} else if (item.id == 5) {
// 科技经纪人
routeData = router.resolve({ path: "/five" });
}
// return window.open(routeData.href, "_blank");
// TODO ...... 切换身份待处理
// await permissionStore.generateRoutes(item.id);
userStore.switchRole(item.id);
// console.log(userStore.roleId);
window.open(routeData.href, "_blank");
// identitySwitch({ identity: item.id - 0 }).then((res) => {
// window.open(routeData.href, "_blank");
// console.log(res);
// });
// store.dispatch("GenerateRoutes", 1).then(() => {
// console.log(localStorage.getItem("select_identity"));
// window.open(routeData.href, "_blank");
// });
};
</script>

View File

@ -0,0 +1,3 @@
<template>
<router-view></router-view>
</template>

View File

@ -0,0 +1 @@
<template>12341test</template>

3
src/views/test.vue Normal file
View File

@ -0,0 +1,3 @@
<template>
<h1>test</h1>
</template>

View File

@ -0,0 +1,10 @@
<template>
<div>
{{ msg }}
</div>
</template>
<script setup>
import { ref } from "vue";
const msg = ref("hekko");
</script>

View File

@ -0,0 +1,353 @@
<template>
<div class="login">
<div class="content">
<img class="loginImg" src="@/assets/images/login.png" alt="" srcset="" />
<div class="login-form">
<el-tabs v-model="loginForm.mode" v-if="isLogin == 1">
<el-tab-pane label="账号密码登录" name="102"></el-tab-pane>
<el-tab-pane label="手机快捷登录" name="101"></el-tab-pane>
</el-tabs>
<div v-if="isLogin == 1">
<el-form ref="loginRef" :model="loginForm" :rules="loginRules">
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
type="text"
auto-complete="off"
placeholder="手机号"
>
<template #prefix>
<svg-icon
icon-class="user"
class="el-input__icon input-icon"
/>
</template>
</el-input>
</el-form-item>
<el-form-item prop="password" v-if="loginForm.mode == 102">
<el-input
v-model="loginForm.password"
type="password"
auto-complete="off"
placeholder="密码"
@keyup.enter="handleLogin"
>
<template #prefix>
<svg-icon
icon-class="password"
class="el-input__icon input-icon"
/>
</template>
</el-input>
</el-form-item>
<el-form-item prop="captcha" v-if="loginForm.mode == 101">
<el-input
v-model="loginForm.captcha"
auto-complete="off"
placeholder="验证码"
style="width: 65%"
@keyup.enter="handleLogin"
>
<template #prefix>
<svg-icon
icon-class="validCode"
class="el-input__icon input-icon"
/>
</template>
</el-input>
<WebGetCode v-model:mobile="loginForm.username" />
</el-form-item>
<el-form-item>
<el-checkbox v-model="loginForm.rememberMe">记住账号</el-checkbox>
</el-form-item>
<el-form-item style="width: 100%">
<el-button
class="x_btns"
:loading="loading"
size="default"
type="primary"
style="width: 100%"
@click.prevent="handleLogin"
>
<span v-if="!loading"> </span>
<span v-else> 中...</span>
</el-button>
</el-form-item>
</el-form>
<div style="overflow: hidden">
<div style="float: left" v-if="isLogin == 1">
<span class="text_col pointer" :to="'/login'" @click="isLogin = 3"
>忘记密码</span
>
</div>
<div style="float: right" v-if="register">
<span @click="isLogin = 2" style="color: #0054ff" class="pointer"
>注册</span
>
</div>
</div>
<el-form
v-if="isLogin == 1"
:model="registerForm"
status-icon
:rules="isCheckRules"
ref="isCheck"
>
<el-form-item prop="isCheck">
<div style="margin-top: 43px">
<div class="text_col">
<div>其他登录方式</div>
<img
class="pointer"
src="../../../assets/images/weixin.png"
alt=""
/>
</div>
<el-checkbox
v-model="registerForm.isCheck"
style="margin-top: 43px"
>
<span class="text_col">登录即同意</span
><span style="color: #0054ff">协议</span>
</el-checkbox>
</div>
</el-form-item>
</el-form>
</div>
<Register v-model:isLogin="isLogin" v-else-if="isLogin == 2" />
<Retrieve v-model:isLogin="isLogin" v-else-if="isLogin == 3" />
</div>
</div>
<!-- 底部 -->
<div class="foot">Copyright © 2007-2021 中科云 版权所有</div>
</div>
</template>
<script setup>
import Cookies from "js-cookie";
import { encrypt, decrypt } from "@/utils/jsencrypt";
import md5 from "js-md5";
import Register from "../register";
import Retrieve from "../retrieve";
import WebGetCode from "@/components/webGetCode";
import useUserStore from "@/store/modules/user";
// const store = useStore();
const router = useRouter();
const userStore = useUserStore();
const { proxy } = getCurrentInstance();
const isLogin = ref(1);
console.log(md5);
const loginForm = ref({
username: "18212342345",
password: "12342345",
rememberMe: false,
mode: "102",
captcha: "",
});
const registerForm = ref({
isCheck: true,
});
const disabled = ref(true);
const buttonName = ref("获取验证码");
const isDisabled = ref(false);
const time = ref(10);
const loginRules = {
username: [{ required: true, trigger: "blur", message: "请输入您的手机号" }],
password: [{ required: true, trigger: "blur", message: "请输入您的密码" }],
// captcha: [{ required: true, trigger: "change", message: "请输入验证码" }],
};
const isCheck = (rule, value, callback) => {
if (!value) {
callback(new Error("请阅读并勾选"));
} else {
callback();
}
};
const isCheckRules = {
isCheck: [{ required: true, validator: isCheck, trigger: "change" }],
};
// const codeUrl = ref("");
const loading = ref(false);
// 注册开关
const register = ref(true);
const redirect = ref(undefined);
function handleLogin() {
proxy.$refs.loginRef.validate((valid) => {
proxy.$refs.isCheck.validate((valid2) => {
if (valid && valid2) {
loading.value = true;
if (loginForm.value.rememberMe) {
Cookies.set("username", loginForm.value.username, { expires: 30 });
Cookies.set("password", loginForm.value.password, {
expires: 30,
});
Cookies.set("rememberMe", loginForm.value.rememberMe, {
expires: 30,
});
} else {
// 否则移除
Cookies.remove("username");
Cookies.remove("password");
Cookies.remove("rememberMe");
}
// 调用action的登录方法
let formData = Object.assign({}, loginForm.value);
formData.password = md5(formData.password);
console.log("123435");
formData.mode = formData.mode - 0;
userStore
.login(formData)
.then(() => {
router.push({ path: redirect.value || "/" });
})
.catch(() => {
loading.value = false;
});
}
});
});
}
function getCookie() {
const username = Cookies.get("username");
const password = Cookies.get("password");
const rememberMe = Cookies.get("rememberMe");
loginForm.value = {
username: username === undefined ? loginForm.value.username : username,
password: password === undefined ? loginForm.value.password : password,
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe),
mode: loginForm.value.mode,
};
}
getCookie();
</script>
<style lang="scss" scoped>
.login {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
background: #b1ccff;
.content {
// padding: 0 250px;
// padding: 0 30px;
width: 1400px;
margin: 0 auto;
flex: 1;
background: #b1ccff;
display: flex;
align-items: center;
justify-content: space-between;
.loginImg {
width: 705px;
height: 458px;
}
}
.foot {
text-align: center;
padding: 20px;
background: #fff;
font-size: 14px;
font-family: Source Han Sans CN;
font-weight: 500;
color: #666666;
}
}
.title {
margin: 0px auto 30px auto;
text-align: center;
color: #707070;
}
.login-form {
width: 516px;
min-width: 455px;
// height: 517px;
background: #ffffff;
box-sizing: border-box;
padding: 30px 33px;
.tit {
padding: 30px 0;
text-align: center;
font-size: 20px;
font-family: Source Han Sans CN;
font-weight: bold;
color: #333333;
}
.el-tab-pane {
padding-top: 10px;
}
.el-input {
height: 38px;
input {
height: 38px;
}
}
.input-icon {
height: 39px;
width: 14px;
margin-left: 2px;
}
::v-deep .el-tabs__active-bar {
background-color: #0054ff;
}
::v-deep .el-tabs__item {
color: #666666;
font-size: 16px;
&.is-active {
font-weight: 500;
color: #0054ff;
font-size: 20px;
}
}
}
.login-tip {
font-size: 13px;
text-align: center;
color: #bfbfbf;
}
.login-code {
width: 30%;
height: 38px;
float: right;
img {
cursor: pointer;
vertical-align: middle;
}
}
.el-login-footer {
height: 40px;
line-height: 40px;
position: fixed;
bottom: 0;
width: 100%;
text-align: center;
color: #fff;
font-family: Arial;
font-size: 12px;
letter-spacing: 1px;
}
.login-code-img {
height: 36px;
}
.text_col {
color: #999999;
img {
width: 30px;
margin: 10px 0 0 10px;
}
}
</style>

View File

@ -0,0 +1,235 @@
<template>
<el-form
ref="registerRef"
:model="registerForm"
:rules="registerRules"
class="register-form"
>
<h3 class="title">注册</h3>
<el-form-item prop="username">
<el-input
v-model="registerForm.username"
type="text"
maxlength="11"
auto-complete="off"
placeholder="手机号"
>
<template #prefix
><svg-icon icon-class="user" class="el-input__icon input-icon"
/></template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="registerForm.password"
type="password"
auto-complete="off"
placeholder="密码"
@keyup.enter="handleRegister"
>
<template #prefix
><svg-icon icon-class="password" class="el-input__icon input-icon"
/></template>
</el-input>
</el-form-item>
<el-form-item prop="repeat_pass">
<el-input
v-model="registerForm.repeat_pass"
type="password"
auto-complete="off"
placeholder="确认密码"
@keyup.enter="handleRegister"
>
<template #prefix
><svg-icon icon-class="password" class="el-input__icon input-icon"
/></template>
</el-input>
</el-form-item>
<el-form-item prop="captcha" style="margin-bottom: 0">
<el-input
v-model="registerForm.captcha"
auto-complete="off"
placeholder="验证码"
style="width: 65%"
@keyup.enter="handleRegister"
>
<template #prefix
><svg-icon icon-class="validCode" class="el-input__icon input-icon"
/></template>
</el-input>
<webGetCode v-model:username="registerForm.username" />
</el-form-item>
<el-form-item prop="isCheck" style="margin: 10px 0; height: 28px">
<el-checkbox v-model="registerForm.isCheck">
<span style="color: #999999">勾选即同意</span
><span style="color: #0054ff">协议</span>
</el-checkbox>
</el-form-item>
<el-form-item style="width: 100%; margin-top: 35px">
<el-button
class="x_btns"
:loading="loading"
size="default"
type="primary"
style="width: 100%"
@click.prevent="handleRegister"
>
<span v-if="!loading"> </span>
<span v-else> 中...</span>
</el-button>
<div style="float: right">
<span
class="x_blue pointer"
@click="handleLogin"
style="font-size: 16px"
>登录</span
>
</div>
</el-form-item>
</el-form>
</template>
<script setup>
import { ElMessageBox } from "element-plus";
import { register } from "@/api/login";
import webGetCode from "@/components/webGetCode";
import md5 from "js-md5";
const props = defineProps({
isLogin: Number,
});
const emit = defineEmits();
function handleLogin() {
emit("update:isLogin", 1);
}
const router = useRouter();
const { proxy } = getCurrentInstance();
const registerRef = ref();
const registerForm = ref({
username: "",
password: "",
repeat_pass: "",
captcha: "",
isCheck: true,
});
const equalToPassword = (rule, value, callback) => {
if (registerForm.value.password !== value) {
callback(new Error("两次输入的密码不一致"));
} else {
callback();
}
};
const isCheck = (rule, value, callback) => {
if (!value) {
callback(new Error("请阅读并勾选"));
} else {
callback();
}
};
const registerRules = {
username: [
{ required: true, message: "手机号不能为空", trigger: "blur" },
{
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
message: "请输入正确的手机号码",
trigger: "blur",
},
],
password: [
{ required: true, trigger: "blur", message: "请输入您的密码" },
{
min: 5,
max: 20,
message: "用户密码长度必须介于 5 和 20 之间",
trigger: "blur",
},
],
repeat_pass: [
{ required: true, trigger: "blur", message: "请再次输入您的密码" },
{ required: true, validator: equalToPassword, trigger: "blur" },
],
// captcha: [{ required: true, trigger: "change", message: "请输入验证码" }],
isCheck: [{ required: true, validator: isCheck, trigger: "change" }],
};
const codeUrl = ref("");
const loading = ref(false);
function handleRegister() {
registerRef.value.validate((valid) => {
if (valid) {
loading.value = true;
let formData = Object.assign({}, registerForm.value);
formData.password = md5(formData.password);
formData.repeat_pass = md5(formData.repeat_pass);
register(formData)
.then((res) => {
const username = formData.username;
ElMessageBox.alert(
"<font color='red'>恭喜你,您的账号 " +
username +
" 注册成功!</font>",
"系统提示",
{
dangerouslyUseHTMLString: true,
type: "success",
}
)
.then(() => {
handleLogin();
})
.catch(() => {});
})
.catch(() => {
loading.value = false;
});
}
});
}
</script>
<style lang="scss" scoped>
.title {
margin: 0px auto 40px auto;
text-align: center;
color: #333333;
font-size: 20px;
font-weight: bold;
}
.register-form {
.el-input {
height: 38px;
input {
height: 38px;
}
}
.input-icon {
height: 39px;
width: 14px;
margin-left: 2px;
}
}
.register-tip {
font-size: 13px;
text-align: center;
color: #bfbfbf;
}
.el-register-footer {
height: 40px;
line-height: 40px;
position: fixed;
bottom: 0;
width: 100%;
text-align: center;
color: #fff;
font-family: Arial;
font-size: 12px;
letter-spacing: 1px;
}
</style>

View File

@ -0,0 +1,266 @@
<template>
<div>
<el-form
v-if="step == 1"
ref="mobileRef"
:model="retrieveForm"
:rules="mobileRules"
class="retrieve-form"
>
<h3 class="title">
<el-icon
class="pointer"
:size="18"
color="#333333"
@click="handleLogin"
>
<Back />
</el-icon>
<span>找回密码</span>
</h3>
<el-form-item prop="mobile">
<el-input
v-model="retrieveForm.mobile"
type="text"
auto-complete="off"
placeholder="手机号"
>
<template #prefix
><svg-icon icon-class="user" class="el-input__icon input-icon"
/></template>
</el-input>
</el-form-item>
<el-form-item prop="captcha" style="margin-bottom: 0">
<el-input
v-model="retrieveForm.captcha"
auto-complete="off"
placeholder="验证码"
style="width: 65%"
>
<template #prefix
><svg-icon icon-class="validCode" class="el-input__icon input-icon"
/></template>
</el-input>
<WebGetCode v-model:mobile="retrieveForm.mobile" />
<!-- @isSuccess="isSuccess" -->
</el-form-item>
<el-form-item style="width: 100%; margin-top: 35px">
<!-- <el-button
class="x_btns"
size="medium"
type="primary"
style="width: 100%"
@click.prevent="handleNext"
>
<span>下一步</span>
</el-button> -->
</el-form-item>
</el-form>
<el-form
v-else
ref="passRef"
:model="retrieveForm"
:rules="passRules"
class="retrieve-form"
>
<h3 class="title">
<el-icon class="pointer" :size="18" color="#333333" @click="step--">
<Back />
</el-icon>
<span>找回密码</span>
</h3>
<el-form-item prop="password">
<el-input
v-model="retrieveForm.password"
type="password"
auto-complete="off"
placeholder="请输入新密码"
>
<template #prefix
><svg-icon icon-class="password" class="el-input__icon input-icon"
/></template>
</el-input>
</el-form-item>
<el-form-item prop="repeat_pass">
<el-input
v-model="retrieveForm.repeat_pass"
type="password"
auto-complete="off"
placeholder="确认密码"
>
<template #prefix
><svg-icon icon-class="password" class="el-input__icon input-icon"
/></template>
</el-input>
</el-form-item>
<el-form-item style="width: 100%; margin-top: 35px">
<el-button
class="x_btns"
size="default"
type="primary"
style="width: 100%"
@click.prevent="handleRetrieve"
>
<span> </span>
</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script setup>
import { ElMessageBox } from "element-plus";
import { getCodeImg, resetPassword } from "@/api/login";
import WebGetCode from "@/components/webGetCode";
const props = defineProps({
isLogin: Number,
});
const emit = defineEmits();
function handleLogin() {
emit("update:isLogin", 1);
}
const router = useRouter();
const { proxy } = getCurrentInstance();
const retrieveForm = ref({
mobile: "",
password: "",
repeat_pass: "",
captcha: "",
token: "",
});
const equalToPassword = (rule, value, callback) => {
if (retrieveForm.value.password !== value) {
callback(new Error("两次输入的密码不一致"));
} else {
callback();
}
};
const mobileRules = {
mobile: [
{ required: true, message: "手机号不能为空", trigger: "blur" },
{
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
message: "请输入正确的手机号码",
trigger: "blur",
},
],
captcha: [{ required: true, trigger: "change", message: "请输入验证码" }],
};
const passRules = {
password: [
{ required: true, trigger: "blur", message: "请输入您的密码" },
{
min: 5,
max: 20,
message: "用户密码长度必须介于 5 和 20 之间",
trigger: "blur",
},
],
repeat_pass: [
{ required: true, trigger: "blur", message: "请再次输入您的新密码" },
{ required: true, validator: equalToPassword, trigger: "blur" },
],
};
const step = ref(1);
// function handleNext() {
// proxy.$refs.mobileRef.validate((valid) => {
// if (valid) {
// codeValid(retrieveForm.value).then((res) => {
// if (res.code == 200) {
// retrieveForm.value.token = res.data.token;
// step.value = 2;
// }
// });
// }
// });
// }
function handleRetrieve() {
proxy.$refs.passRef.validate((valid) => {
if (valid) {
let formData = Object.assign({}, retrieveForm.value);
formData.password = proxy.md5(formData.password);
formData.repeat_pass = proxy.md5(formData.repeat_pass);
resetPassword(formData)
.then((res) => {
const mobile = formData.mobile;
ElMessageBox.alert(
"<font color='red'>恭喜你,您的账号 " +
mobile +
" 修改密码成功!</font>",
"系统提示",
{
dangerouslyUseHTMLString: true,
type: "success",
}
)
.then(() => {
handleLogin();
// router.push("/login");
})
.catch(() => {});
})
.catch(() => {});
}
});
}
</script>
<style lang='scss' scoped>
.title {
display: flex;
align-items: center;
margin: 0px auto 40px auto;
color: #333333;
font-size: 20px;
font-weight: bold;
.el-icon {
text-align: left;
}
span {
flex: 1;
text-align: center;
}
}
.retrieve-form {
.el-input {
height: 38px;
input {
height: 38px;
}
}
.input-icon {
height: 39px;
width: 14px;
margin-left: 2px;
}
}
.register-tip {
font-size: 13px;
text-align: center;
color: #bfbfbf;
}
.el-register-footer {
height: 40px;
line-height: 40px;
position: fixed;
bottom: 0;
width: 100%;
text-align: center;
color: #fff;
font-family: Arial;
font-size: 12px;
letter-spacing: 1px;
}
</style>

View File

@ -0,0 +1,26 @@
<template>
<div class="index">
<WebsiteHeader></WebsiteHeader>
<div class="content">
<router-view />
</div>
<el-backtop />
</div>
</template>
<script setup name="WebsiteLayout">
import WebsiteHeader from "@/components/WebsiteHeader";
</script>
<style lang="scss" scoped>
.index {
position: relative;
height: 100%;
width: 100%;
.content {
width: 100%;
padding-top: 80px;
height: 100%;
}
}
</style>

View File

@ -31,7 +31,7 @@ export default defineConfig(({ mode, command }) => {
proxy: {
// https://cn.vitejs.dev/config/#server-proxy
'/dev-api': {
target: 'http://localhost:8080',
target: 'http://172.20.10.10:1618',
changeOrigin: true,
rewrite: (p) => p.replace(/^\/dev-api/, '')
}