bug fix and performance improvements

This commit is contained in:
quantulr
2023-08-01 15:50:13 +08:00
parent 1f5d58558b
commit 0a48c30f0e
37 changed files with 4661 additions and 209 deletions

View File

@ -7,15 +7,21 @@
<meta content="webkit" name="renderer">
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<link href="/favicon.ico" rel="icon">
<!-- <script type="text/javascript">-->
<!-- window._AMapSecurityConfig = {-->
<!-- securityJsCode: '7d1b758db4876389af608259c4c99832',-->
<!-- }-->
<!-- </script>-->
<script type="text/javascript">
window._AMapSecurityConfig = {
securityJsCode: '7d1b758db4876389af608259c4c99832',
securityJsCode: '2b65e7751cb17e4605f4c4cdccf885f6',
// securityJsCode: '7d1b758db4876389af608259c4c99832',
}
</script>
<script src='//webapi.amap.com/maps?v=1.4.15&key=bfc7224388e835679ffe74cfbd28c8d6' type="text/javascript"></script>
<script src="https://webapi.amap.com/maps?v=1.4.11&key=bfc7224388e835679ffe74cfbd28c8d6&plugin=AMap.DistrictSearch"
type="text/javascript"></script>
<script src="//webapi.amap.com/ui/1.0/main.js?v=1.0.11"></script>
<!-- <script src='//webapi.amap.com/maps?v=1.4.15&key=bfc7224388e835679ffe74cfbd28c8d6' type="text/javascript"></script>-->
<!-- <script src="https://webapi.amap.com/maps?v=1.4.11&key=bfc7224388e835679ffe74cfbd28c8d6&plugin=AMap.DistrictSearch"-->
<!-- type="text/javascript"></script>-->
<!-- <script src="//webapi.amap.com/ui/1.0/main.js?v=1.0.11"></script>-->
<title>中科云</title>
<!--[if lt IE 11]>
<script>window.location.href = '/html/ie.html';</script><![endif]-->

View File

@ -17,6 +17,7 @@
"url": "https://gitee.com/y_project/RuoYi-Vue.git"
},
"dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1",
"@element-plus/icons-vue": "1.1.4",
"@vueuse/core": "8.5.0",
"@wangeditor/editor": "^5.1.23",
@ -29,6 +30,7 @@
"element-plus": "2.1.8",
"file-saver": "2.0.5",
"fuse.js": "6.5.3",
"html2canvas": "^1.4.1",
"js-cookie": "3.0.1",
"js-md5": "^0.7.3",
"jsencrypt": "3.2.1",

3715
src/assets/custom.geo.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

3
src/assets/geo_ru.json Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 508 B

File diff suppressed because one or more lines are too long

View File

@ -138,7 +138,7 @@
{{ t("headerMenu.personalCenter") }}
</div>
<div
v-if="userStore.avatar"
v-if="userStore.token"
class="menu-item-tit"
:class="pagePath == '/login' ? 'active' : ''"
@click="logout"
@ -181,7 +181,6 @@
</template>
<script setup>
import { ElMessageBox } from "element-plus";
import { reactive, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
@ -190,6 +189,7 @@ import { getCategory } from "@/api/website/solution";
import { useI18n } from "vue-i18n";
import useSettingsStore from "@/store/modules/settings";
import defaultAvatar from "@/assets/logo/avatar.png";
import modal from "@/plugins/modal";
const userStore = useUserStore();
let state = reactive({});
@ -245,11 +245,8 @@ function handlePath(path) {
}
function logout() {
ElMessageBox.confirm("确定注销并退出系统吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
modal
.confirm(t("common.areYouSureYouWantToLogOutAndExitTheSystem"))
.then(() => {
useUserStore()
.logOut()

View File

@ -131,7 +131,7 @@ function submitForm() {
if (valid) {
pubMessage(form.value).then((res) => {
// proxy.$modal.msgSuccess(res.message);
ElMessage.success("发送成功");
ElMessage.success(t("tips.sendSuccess"));
reset();
});
}

View File

@ -115,5 +115,7 @@ const form = {
achievementPicture: "Изображение достижений",
// 产品领域
productField: "Область продукта",
// 成熟度证明材料
maturityProofMaterial: "Материалы подтверждения зрелости",
};
export default form;

View File

@ -113,6 +113,8 @@ const form = {
achievementPicture: "成果图片",
// 产品领域
productField: "产品领域",
// 成熟度证明材料
maturityProofMaterial: "成熟度证明材料",
};
export default form;

View File

@ -9,20 +9,28 @@ const tips = {
reasonDescription: "Описание причины незавершенного проекта",
browseEnterpriseInformation: "Просмотр информации о предприятии",
moveToDraftBox: "Переместить в черновик",
// 取消关联成功 取消发布成功 认领专利成功
cancelAssociationSuccess: "Отмена связи прошла успешно",
cancelReleaseSuccess: "Отмена публикации прошла успешно",
claimPatentSuccess: "Патент успешно принят",
// 解绑实验室成功
unbindLabSuccess: "Успешно отвязана лаборатория",
// 导入结果
importResult: "Результат импорта",
// 面议
faceToFace: "По договоренности",
// 更新企业信息成功
updateEnterpriseInformationSuccess:
"Информация о предприятии успешно обновлена",
pleaseEnterApplicant: "Пожалуйста, введите заявителя (разделите запятыми)",
sendSuccess: "Отправлено успешно",
applyForSettledSuccessfully: "Заявка на въезд успешно подана",
reLogin: "Перезагрузить",
loginStatusExpired:
"Срок действия сеанса истек, вы можете остаться на этой странице или перезагрузить страницу",
invalidSessionOrSessionExpired:
"Недействительный сеанс или сеанс истек, пожалуйста, войдите снова.",
downloadingData: "Загрузка данных, пожалуйста, подождите",
backendInterfaceConnectionException:
"Ошибка подключения к интерфейсу бэкэнда",
// 下载文件出现错误,请联系管理员!
downloadFileError:
"Ошибка загрузки файла, пожалуйста, свяжитесь с администратором!",
};
export default tips;

View File

@ -9,19 +9,23 @@ const tips = {
reasonDescription: "未结题原因描述",
browseEnterpriseInformation: "浏览企业信息",
moveToDraftBox: "移到草稿箱",
// 取消关联成功 取消发布成功 认领专利成功
cancelAssociationSuccess: "取消关联成功",
cancelReleaseSuccess: "取消发布成功",
claimPatentSuccess: "认领专利成功",
// 解绑实验室成功
unbindLabSuccess: "解绑实验室成功",
// 导入结果
importResult: "导入结果",
// 面议
faceToFace: "面议",
// 更新企业信息成功
updateEnterpriseInformationSuccess: "更新企业信息成功",
pleaseEnterApplicant: "请输入申请(专利权)人(多个请以 , 分割)",
sendSuccess: "发送成功",
applyForSettledSuccessfully: "申请入驻成功",
reLogin: "重新登录",
loginStatusExpired: "登录状态已过期,您可以继续留在该页面,或者重新登录",
invalidSessionOrSessionExpired: "无效的会话,或者会话已过期,请重新登录。",
downloadingData: "正在下载数据,请稍候",
backendInterfaceConnectionException: "后端接口连接异常",
// 下载文件出现错误,请联系管理员!
downloadFileError: "下载文件出现错误,请联系管理员!",
};
export default tips;

View File

@ -93,7 +93,6 @@
</template>
<script setup>
import { ElMessageBox } from "element-plus";
import Breadcrumb from "@/components/Breadcrumb";
import TopNav from "@/components/TopNav";
import Hamburger from "@/components/Hamburger";
@ -101,20 +100,21 @@ import Screenfull from "@/components/Screenfull";
import SizeSelect from "@/components/SizeSelect";
import HeaderSearch from "@/components/HeaderSearch";
import UserAvatar from "@/views/system/user/profile/userAvatar.vue";
import RuoYiGit from "@/components/RuoYi/Git";
import RuoYiDoc from "@/components/RuoYi/Doc";
import useAppStore from "@/store/modules/app";
import useUserStore from "@/store/modules/user";
import useSettingsStore from "@/store/modules/settings";
import { useI18n } from "vue-i18n";
import { ref } from "vue";
import defaultAvatar from "@/assets/logo/avatar.png";
import modal from "@/plugins/modal";
const appStore = useAppStore();
const userStore = useUserStore();
const settingsStore = useSettingsStore();
const { t, locale } = useI18n();
const avatarRef = ref();
const baseUrl = ref(import.meta.env.VITE_APP_BASE_API);
function toggleSideBar() {
appStore.toggleSideBar();
}
@ -139,11 +139,8 @@ function handleCommand(command) {
}
function logout() {
ElMessageBox.confirm("确定注销并退出系统吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
modal
.confirm(t("common.areYouSureYouWantToLogOutAndExitTheSystem"))
.then(() => {
userStore.logOut().then(() => {
location.href = "/index";

View File

@ -35,26 +35,118 @@ export const enterpriseOptions = [
{ key: "104", value: "高新技术企业" },
{ key: "105", value: "科技企业" },
];
// 基金对接-Фонд стыкуется
// 委托研发-Поручить разработку
// 海外留学生培养-Обучение иностранных студентов
// 贷款-Кредиты
// 对接专家院士-Академик - специалист по стыковке
// 人才培养-Подготовка кадров
// 一带一路国际合作-Международное сотрудничество
// 上市辅导-Консультирование по листингу
// 成果产业化-Индустриализация результатов
// 国家级科研平台合作-Сотрудничество национальных научно - исследовательских платформ
// 研发项目立项评估-Оценка проектов НИОКР
// 科技查新-Новые технологии.
// 产业链上下游对接-Промышленная цепочка вверх и вниз по течению
// 知识产权布局-Распределение интеллектуальной собственности
// 高价值专利培育-Высокая стоимость патентов
// 技术咨询-Технические консультации
// 对接政府项目落地-Посадка на правительственный проект
// 设立院士工作站-Создание академической рабочей станции
// 需求类型
export const demandCategoryList = [
{ id: 1, name: "基金对接" },
{ id: 2, name: "贷款" },
{ id: 3, name: "对接专家院士" },
{ id: 4, name: "人才培养" },
{ id: 5, name: "一带一路国际合作" },
{ id: 6, name: "上市辅导" },
{ id: 7, name: "成果产业化" },
{ id: 8, name: "国家级科研平台合作" },
{ id: 9, name: "研发项目立项评估" },
{ id: 10, name: "科技查新" },
{ id: 11, name: "产业链上下游对接" },
{ id: 12, name: "委托研发" },
{ id: 13, name: "对接政府项目落地" },
{ id: 14, name: "技术咨询" },
{ id: 15, name: "高价值专利培育" },
{ id: 16, name: "知识产权布局" },
{ id: 17, name: "设立院士工作站" },
{ id: 18, name: "海外留学生培养" },
{
id: 1,
name: "基金对接",
nameRu: "Фонд стыкуется",
},
{
id: 2,
name: "贷款",
nameRu: "Поручить разработку",
},
{
id: 3,
name: "对接专家院士",
nameRu: "Обучение иностранных студентов",
},
{
id: 4,
name: "人才培养",
nameRu: "Кредиты",
},
{
id: 5,
name: "一带一路国际合作",
nameRu: "Академик - специалист по стыковке",
},
{
id: 6,
name: "上市辅导",
nameRu: "Подготовка кадров",
},
{
id: 7,
name: "成果产业化",
nameRu: "Международное сотрудничество",
},
{
id: 8,
name: "国家级科研平台合作",
nameRu: "Консультирование по листингу",
},
{
id: 9,
name: "研发项目立项评估",
nameRu: "Индустриализация результатов",
},
{
id: 10,
name: "科技查新",
nameRu: "Сотрудничество национальных научно - исследовательских платформ",
},
{
id: 11,
name: "产业链上下游对接",
nameRu: "Оценка проектов НИОКР",
},
{
id: 12,
name: "委托研发",
nameRu: "Новые технологии.",
},
{
id: 13,
name: "对接政府项目落地",
nameRu: "Промышленная цепочка вверх и вниз по течению",
},
{
id: 14,
name: "技术咨询",
nameRu: "Распределение интеллектуальной собственности",
},
{
id: 15,
name: "高价值专利培育",
nameRu: "Высокая стоимость патентов",
},
{
id: 16,
name: "知识产权布局",
nameRu: "Технические консультации",
},
{
id: 17,
name: "设立院士工作站",
nameRu: "Посадка на правительственный проект",
},
{
id: 18,
name: "海外留学生培养",
nameRu: "Создание академической рабочей станции",
},
];
// export const enterpriseOptions = [
// { key: "101", value: '上市企业' },

View File

@ -109,10 +109,10 @@ service.interceptors.response.use(
if (!isRelogin.show) {
isRelogin.show = true;
ElMessageBox.confirm(
"登录状态已过期,您可以继续留在该页面,或者重新登录",
t("tips.loginStatusExpired"),
t("common.systemPrompt"),
{
confirmButtonText: "重新登录",
confirmButtonText: t("tips.reLogin"),
cancelButtonText: t("common.cancel"),
type: "warning",
}
@ -129,7 +129,7 @@ service.interceptors.response.use(
isRelogin.show = false;
});
}
return Promise.reject("无效的会话,或者会话已过期,请重新登录。");
return Promise.reject(t("tips.invalidSessionOrSessionExpired"));
} else if (code === 500) {
ElMessage({
message: msg,
@ -149,7 +149,7 @@ service.interceptors.response.use(
console.log("err" + error);
let { message } = error;
if (message == "Network Error") {
message = "后端接口连接异常";
message = t("tips.backendInterfaceConnectionException");
} else if (message.includes("timeout")) {
message = t("common.requestTimeout");
} else if (message.includes("Request failed with status code")) {
@ -167,7 +167,7 @@ service.interceptors.response.use(
// 通用下载方法
export function download(url, params, filename) {
downloadLoadingInstance = ElLoading.service({
text: "正在下载数据,请稍候",
text: t("tips.downloadingData"),
background: "rgba(0, 0, 0, 0.7)",
});
return service
@ -196,7 +196,7 @@ export function download(url, params, filename) {
})
.catch((r) => {
console.error(r);
ElMessage.error("下载文件出现错误,请联系管理员!");
ElMessage.error(t("tips.downloadFileError"));
downloadLoadingInstance.close();
});
}

5
src/utils/string.js Normal file
View File

@ -0,0 +1,5 @@
export function toTitleCase(str) {
return str.replace(/\w\S*/g, function (txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
}

View File

@ -37,22 +37,22 @@ export default {
data: {
type: Array,
default: () => [
{
name: "测试一类",
value: 30,
},
{
name: "测试二类",
value: 30,
},
{
name: "测试三类",
value: 30,
},
{
name: "测试四类",
value: 30,
},
// {
// name: "测试一类",
// value: 30,
// },
// {
// name: "测试二类",
// value: 30,
// },
// {
// name: "测试三类",
// value: 30,
// },
// {
// name: "测试四类",
// value: 30,
// },
],
},
},
@ -196,7 +196,8 @@ button {
color: green;
text-decoration: none;
font-size: 16px;
font-family: "微软雅黑";
//font-family: "微软雅黑";
font-family: "Microsoft YaHei UI";
font-weight: bold;
}
.wordCloud__tag :hover {

View File

@ -26,8 +26,20 @@
<el-checkbox
v-for="item in checkList"
:key="item.id"
:label="item.name"
>{{ item.name }}
:label="
locale === 'zh'
? item.name
: locale === 'ru'
? item.nameRu
: null
"
>{{
locale === "zh"
? item.name
: locale === "ru"
? item.nameRu
: null
}}
</el-checkbox>
<!-- <el-checkbox label="0" @change="handleCheck">其他</el-checkbox> -->
</el-checkbox-group>
@ -107,34 +119,34 @@
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item
:label="t('admin.table.demandSubmitter')"
prop="commitUserName"
>
<el-input
v-model="form.commitUserName"
:placeholder="
t('admin.form.placeholder', {
type: t('admin.table.demandSubmitter'),
})
"
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
:label="t('admin.form.demandSubmitterPhone')"
prop="commitPhone"
>
<el-input
v-model="form.commitPhone"
:placeholder="t('admin.form.demandSubmitterPhone')"
></el-input>
</el-form-item>
</el-col>
</el-row>
<!-- <el-row>-->
<!-- <el-col :span="12">-->
<!-- <el-form-item-->
<!-- :label="t('admin.table.demandSubmitter')"-->
<!-- prop="commitUserName"-->
<!-- >-->
<!-- <el-input-->
<!-- v-model="form.commitUserName"-->
<!-- :placeholder="-->
<!-- t('admin.form.placeholder', {-->
<!-- type: t('admin.table.demandSubmitter'),-->
<!-- })-->
<!-- "-->
<!-- ></el-input>-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- <el-col :span="12">-->
<!-- <el-form-item-->
<!-- :label="t('admin.form.demandSubmitterPhone')"-->
<!-- prop="commitPhone"-->
<!-- >-->
<!-- <el-input-->
<!-- v-model="form.commitPhone"-->
<!-- :placeholder="t('admin.form.demandSubmitterPhone')"-->
<!-- ></el-input>-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- </el-row>-->
</el-form>
<div :style="{ marginLeft: labelWidth + 'px' }">
<el-button @click="backToList"
@ -154,14 +166,15 @@ import {
insertDemand,
updateDemand,
} from "@/api/admin/enterprise/demand";
import { toTitleCase } from "@/utils/string";
import { ElMessage } from "element-plus";
import { computed, onMounted, reactive, toRefs } from "vue";
import { computed, onMounted, reactive, ref, toRefs } from "vue";
import { demandCategoryList } from "@/utils/parameter";
import { useRoute, useRouter } from "vue-router";
import { updateCount } from "@/api/admin/count";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const { t, locale } = useI18n();
const router = useRouter();
const route = useRoute();
const data = reactive({
@ -284,9 +297,11 @@ function addCheck() {
return item.name.trim() == checkInput.value.trim();
});
if (!flag) {
const nameField =
locale.value === "zh" ? "name" : `name${toTitleCase(locale.value)}`;
checkList.push({
id: checkList.length + 1,
name: checkInput.value,
[nameField]: checkInput.value,
});
checkInput.value = "";
}
@ -298,12 +313,16 @@ onMounted(() => {
const obj = Object.assign({}, route, { title: "修改服务需求" });
tab.updatePage(obj);
getDemand({ id: route.query.id }).then((resp) => {
// const nameField = locale === "zh" ? "name" : "nameRu";
console.log(locale.value);
const nameField =
locale.value === "zh" ? "name" : `name${toTitleCase(locale.value)}`;
if (resp.data.kinds) {
resp.data.kinds.forEach((el, index) => {
if (!checkList.find((item) => item.name == el)) {
if (!checkList.find((item) => item[nameField] === el)) {
checkList.push({
id: index,
name: el,
[nameField]: el,
});
}
});

View File

@ -371,18 +371,22 @@ const checkList = reactive([
{
id: 1,
name: "成果推广",
nameRu: "Продвижение достижений",
},
{
id: 2,
name: "关键成果解决",
nameRu: "Решение ключевых результатов",
},
{
id: 3,
name: "对接专家院士",
nameRu: "Связь с экспертами и академиками",
},
{
id: 4,
name: "上市辅导",
nameRu: "Помощь в выходе на рынок",
},
]);
@ -460,7 +464,11 @@ onMounted(() => {
formRef.value.resetFields();
if (route.query.id) {
const obj = Object.assign({}, route, { title: "修改技术需求" });
const obj = Object.assign({}, route, {
title: t("admin.form.edit", {
type: t("webSearch.technologyDemand"),
}),
});
tab.updatePage(obj);
getTechnologyDemand({ id: route.query.id }).then((resp) => {
form.value = resp.data;

View File

@ -210,14 +210,21 @@ function handleShelf(row) {
let text =
row.shelf_status == 2 ? t("admin.table.putOn") : t("admin.table.putOff");
modal
.confirm('确认要"' + text + '""' + row.id + '"的需求吗?')
// .confirm('确认要"' + text + '""' + row.id + '"的需求吗?')
.confirm(
t("admin.common.confirmAction", {
action: text,
number: row.id,
type: t("admin.common.demand"),
})
)
.then(function () {
let status = row.shelf_status == 1 ? 2 : 1;
return achievementShelf({ id: row.id, status });
})
.then(() => {
getList();
ElMessage.success(text + "成功");
ElMessage.success(t("admin.common.success", { action: text }));
})
.catch(() => {});
}

View File

@ -191,7 +191,14 @@ function handleQuery() {
// 取消发布
const releaseCancel = (id) => {
modal
.confirm('确认要取消发布id为"' + id + '"的产品吗?')
// .confirm('确认要取消发布id为"' + id + '"的产品吗?')
.confirm(
t("admin.common.confirmAction", {
action: t("admin.table.cancelPublish"),
number: id,
type: t("admin.form.product"),
})
)
.then(async () => {
await updateEnterpriseProduct({ id, status: 3 });
ElMessage.success(t("tips.cancelReleaseSuccess"));
@ -206,14 +213,21 @@ const handleShelf = (row) => {
let text =
row.shelfStatus == 2 ? t("admin.table.putOn") : t("admin.table.putOff");
modal
.confirm('确认要"' + text + '""' + row.id + '"的产品吗?')
// .confirm('确认要"' + text + '""' + row.id + '"的产品吗?')
.confirm(
t("admin.common.confirmAction", {
action: text,
number: row.id,
type: t("admin.form.product"),
})
)
.then(function () {
let status = row.shelfStatus == 1 ? 2 : 1;
return updateEnterpriseProduct({ id: row.id, shelfStatus: status });
})
.then(() => {
getList();
ElMessage.success(text + "成功");
ElMessage.success(t("admin.common.success", { action: text }));
})
.catch(() => {});
};

View File

@ -50,7 +50,7 @@ const submitForm = async (is_submit) => {
form.value.cooperationMode = form.value.cooperationModeArr.join(",");
if (route.query.id) {
await updateEnterpriseProduct(form.value);
ElMessage.success("修改产品成功");
ElMessage.success(t("admin.common.editSuccess"));
} else {
await insertEnterpriseProduct(form.value);
ElMessage.success(t("admin.common.AddSuccess"));

View File

@ -268,7 +268,7 @@
</p>
<el-row>
<el-col :span="24">
<el-form-item label="成熟度证明材料:">
<el-form-item :label="t('admin.form.maturityProofMaterial')">
<FileUpload
v-model="modelValue.file"
:fileType="['doc', 'xls', 'ppt', 'txt', 'pdf', 'jpg']"

View File

@ -195,7 +195,11 @@ function handleShelf(row) {
const shelfStatus = row.shelfStatus == 1 ? 2 : 1;
await updateExpertAchievement({ id: row.id, shelfStatus });
getList();
ElMessage.success(text + "成功");
ElMessage.success(
t("admin.common.success", {
action: text,
})
);
});
}

View File

@ -69,13 +69,13 @@
</el-button>
</el-col>
<!-- 无接口 暂时注释 -->
<el-col :span="1.5">
<router-link to="./claimPatent">
<el-button type="primary" plain icon="Plus" size="small"
>{{ t("admin.table.claim") }}
</el-button>
</router-link>
</el-col>
<!-- <el-col :span="1.5">-->
<!-- <router-link to="./claimPatent">-->
<!-- <el-button type="primary" plain icon="Plus" size="small"-->
<!-- >{{ t("admin.table.claim") }}-->
<!-- </el-button>-->
<!-- </router-link>-->
<!-- </el-col>-->
<right-toolbar
v-model:showSearch="showSearch"
@queryTable="getList"
@ -145,7 +145,7 @@
<el-form-item :label="t('webSearch.applicant')" prop="applyName">
<el-input
v-model="form.applyName"
placeholder="请输入申请(专利权)人(多个请以 , 分割)"
:placeholder="t('tips.pleaseEnterApplicant')"
/>
</el-form-item>
<el-form-item

View File

@ -313,13 +313,13 @@
<script setup>
import {
technologyProjectList,
insertTechnologyProject,
deleteTechnologyProjectByIds,
insertTechnologyProject,
technologyProjectList,
updateTechnologyProject,
} from "@/api/admin/expert/research";
import dayjs from "dayjs";
import { ElMessage, ElMessageBox } from "element-plus";
import { ElMessage } from "element-plus";
import modal from "@/plugins/modal";
import { cloneDeep } from "lodash-es";
import { useRouter } from "vue-router";
@ -468,14 +468,25 @@ function handleShelf(row) {
const text =
row.shelfStatus == 2 ? t("admin.table.putOn") : t("admin.table.putOff");
modal
.confirm(`确认要${text}"${row.id}"的数据吗?`)
// .confirm(`确认要${text}"${row.id}"的数据吗?`)
.confirm(
t("admin.common.confirmAction", {
type: t("admin.common.researchProject"),
action: text,
number: row.id,
})
)
.then(() => {
const shelfStatus = row.shelfStatus == 1 ? 2 : 1;
return updateTechnologyProject({ id: row.id, shelfStatus });
})
.then(() => {
getList();
ElMessage.success(text + "成功");
ElMessage.success(
t("admin.common.success", {
action: text,
})
);
})
.catch((err) => {
console.log(err);

View File

@ -40,7 +40,7 @@ const submitForm = async () => {
const valid = await researchFormRef.value.validateForm();
if (valid) {
insertResearch(form.value).then((resp) => {
ElMessage.success("申请入驻成功");
ElMessage.success(t("tips.applyForSettledSuccessfully"));
router.push("/identity/index");
});
} else {

View File

@ -1,22 +1,22 @@
<template>
<div class="paging">
<el-icon
class="prev"
:style="{
cursor: page === 1 ? 'not-allowed' : 'pointer',
color: page === 1 ? '#999' : '#4f93ed',
}"
class="prev"
@click="prev"
>
<ArrowLeftBold />
</el-icon>
<div class="page-num">{{ page }}</div>
<el-icon
class="next"
:style="{
cursor: Math.ceil(total / 5) <= page ? 'not-allowed' : 'pointer',
color: Math.ceil(total / 5) <= page ? '#999' : '#4f93ed',
}"
class="next"
@click="next"
>
<ArrowRightBold />
@ -26,6 +26,8 @@
<script setup>
import { toRefs } from "vue";
import { ArrowLeftBold, ArrowRightBold } from "@element-plus/icons-vue";
const emit = defineEmits(["update:page"]);
const props = defineProps({
total: {
@ -40,7 +42,7 @@ const props = defineProps({
const { total, page } = toRefs(props);
const prev = () => {
if (page.value == 1) return;
if (page.value === 1) return;
emit("update:page", page.value - 1);
};
const next = () => {
@ -50,6 +52,7 @@ const next = () => {
</script>
<style lang="scss" scoped>
.paging {
display: flex;
width: 100px;
z-index: 9999;
position: absolute;
@ -58,6 +61,7 @@ const next = () => {
transform: translateX(-50%);
justify-content: space-between;
align-items: center;
.prev,
.next {
user-select: none;
@ -66,5 +70,9 @@ const next = () => {
width: 30px;
height: 30px;
}
.page-num {
color: white;
}
}
</style>

View File

@ -0,0 +1,527 @@
<script setup>
import AMapLoader from "@amap/amap-jsapi-loader";
import { computed, onMounted, ref, shallowRef, toRefs, watch } from "vue";
import { init, registerMap } from "echarts";
import {
countAchievementByArea,
countAchievementByCity,
countAchievementByProvince,
countDemandByArea,
countDemandByCity,
countDemandByProvince,
countEnterpriseByArea,
countEnterpriseByCity,
countEnterpriseByProvince,
countExpertByArea,
countExpertByCity,
countExpertByProvince,
} from "@/api/website/home";
import backBtnPng from "@/assets/images/map_back.png";
import RegionPagine from "@/views/website/home/comp/RegionPagine.vue";
import html2canvas from "html2canvas";
import anime from "animejs";
import { useI18n } from "vue-i18n";
import geoJSONRU from "@/assets/custom.geo.json";
const { t, locale } = useI18n();
const leftBoxPageNum = ref(1);
const rightBoxPageNum = ref(1);
const mapRef = ref(null);
const districtSearch = shallowRef(null);
const myEcharts = shallowRef(null);
const map = shallowRef(null);
const mapData = ref([]);
const props = defineProps({
mapIndex: {
type: Number,
default: 0,
},
});
const { mapIndex } = toRefs(props);
const emit = defineEmits(["changeMapIndex"]);
const methods = [
{
name: "expert",
title: computed(() => t("map.expertMap")),
byProvince: countExpertByProvince,
byCity: countExpertByCity,
byArea: countExpertByArea,
},
{
name: "technology",
// title: "技术分布地图",
title: computed(() => t("map.technologyMap")),
byProvince: countAchievementByProvince,
byCity: countAchievementByCity,
byArea: countAchievementByArea,
},
{
name: "demand",
// title: "需求分布地图",
title: computed(() => t("map.demandMap")),
byProvince: countDemandByProvince,
byCity: countDemandByCity,
byArea: countDemandByArea,
},
{
name: "enterprise",
// title: "企业分布地图",
title: computed(() => t("map.enterpriseMap")),
byProvince: countEnterpriseByProvince,
byCity: countEnterpriseByCity,
byArea: countEnterpriseByArea,
},
];
const areaCount = ref([]); // 按行政区划统计
const industryCount = ref([]); // 按领域统计
const areaCountPaged = computed(() =>
areaCount.value.slice(
(leftBoxPageNum.value - 1) * 5,
leftBoxPageNum.value * 5
)
);
const industryCountPaged = computed(() =>
industryCount.value.slice(
(rightBoxPageNum.value - 1) * 5,
rightBoxPageNum.value * 5
)
);
const options = {
// nameProperty: 'adcode',
visualMap: {
type: "piecewise",
left: "center", //组件离容器左侧的距离,'left', 'center', 'right','20%'
bottom: "30",
orient: "horizontal", //图例排列方向
padding: 5,
pieces: [
{ gte: 0, lte: 99, label: "99", color: "#CAE9FD" },
{ gte: 100, lte: 299, label: "100-299", color: "#7ED2F7" },
{ gte: 300, lte: 499, label: "299-499", color: "#039DDD" },
{ gte: 500, label: "500", color: "#0D4884" },
],
textStyle: {
color: "#fff",
},
visibility: "off",
},
tooltip: {
//提示框信息
trigger: "item",
formatter: "{b}\n{c}人",
},
series: [
{
type: "map",
name: "中国",
map: "map",
data: [],
},
],
};
const loadAMap = async () => {
const AMap = await AMapLoader.load({
key: "377d7c36dd385e2a722f29d4c6e1ffbf", // 申请好的Web端开发者Key首次调用 load 时必填
version: "2.0", // 指定要加载的 JS API 的版本,缺省时默认为 1.4.15
plugins: [
/*"AMap.DistrictSearch", "AMap.DistrictExplorer"*/
], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
AMapUI: {
// 是否加载 AMapUI缺省不加载
version: "1.1", // AMapUI 缺省 1.1
plugins: [], // 需要加载的 AMapUI ui插件
},
});
map.value = new AMap.Map("container", {
zoom: 4, //级别
center: [108.946609, 34.262324], //中心点坐标
// viewMode: '3D' //使用3D视图
});
map.value.on("click", mapClick);
// districtSearch.value = new AMap.DistrictSearch({
// level: 'province',
// extensions: 'all',
// subdistrict: 1,
// showbiz: false,
// })
};
const loading = ref(false);
const loadChinaDistrict = (adcode) => {
loading.value = true;
AMapUI.loadUI(["geo/DistrictExplorer"], (DistrictExplorer) => {
const districtExplorer = new DistrictExplorer({
map: map, //关联的地图实例
});
districtExplorer.loadAreaNode(adcode, async (error, areaNode) => {
if (error) {
console.error(error);
loading.value = false;
return;
}
const parentAdcode = areaNode.getAdcode();
// 绘制载入的区划节点
const geoJSON = {
type: "FeatureCollection",
features: areaNode.getSubFeatures(),
};
const level = getLevel(adcode);
let result;
try {
if (level === 0) {
result = await methods[mapIndex.value]["byProvince"]();
} else if (level === 1) {
result = await methods[mapIndex.value]["byCity"](adcode);
} else if (level === 2) {
result = await methods[mapIndex.value]["byArea"](adcode);
}
} catch (e) {
loading.value = false;
return;
}
if (result.industry)
industryCount.value = Object.keys(result.industry).map((key) => ({
name: key,
count: result.industry[key],
}));
areaCount.value = result.count ?? [];
mapData.value = geoJSON.features.map((el) => {
const areaProp = el.properties;
return {
adcode: areaProp.adcode,
name: areaProp.name,
value:
result.count.find((el) => el.code == areaProp.adcode)?.count ?? 0,
};
});
registerMap("map", { geoJSON, specialAreas: {} });
options.series[0].data = mapData.value;
myEcharts.value.setOption(options);
loading.value = false;
});
});
};
const loadRussiaDistrict = () => {
registerMap("map", { geoJSON: geoJSONRU, specialAreas: {} });
myEcharts.value.setOption(options);
// TODO:get count
};
// 返回地图上一级
const backMap = () => {
// loadChinaDistrict(parentAdcode)
loadChinaDistrict("100000");
};
onMounted(async () => {
await loadAMap();
myEcharts.value = init(mapRef.value);
myEcharts.value.on("click", mapClick);
if (locale.value === "zh") {
loadChinaDistrict("100000");
} else {
loadRussiaDistrict();
}
});
/**
* 根据adcode判断省市区级别
* @param adcode
* @return {number} 国家: 0 省: 1 市: 2 区: 3
*/
const getLevel = (adcode) => {
adcode = adcode.toString();
if (adcode === "100000") {
return 0;
}
const splitPattern = /(\d{2})(\d{2})(\d{2})/;
const resultArray = adcode.match(splitPattern).slice(1);
// 国家: 0 省: 1 市: 2 区: 3
let result = resultArray.indexOf("00");
if (result === -1) {
result = 3;
}
return result;
};
const mapClick = (ev) => {
const level = getLevel(ev.data.adcode);
if (level === 3) {
return;
}
loadChinaDistrict(ev.data.adcode);
};
const playScrollAnimation = (direction, start) => {
const pageWrap = document.querySelector(".page-wrap");
const rootWrap = document.querySelector(".root-container");
html2canvas(pageWrap).then((canvas) => {
canvas.addEventListener("wheel", (ev) => {
ev.stopPropagation();
ev.preventDefault();
});
if (direction === "down") {
rootWrap.insertBefore(canvas, pageWrap);
} else if (direction === "up") {
rootWrap.appendChild(canvas);
rootWrap.style.transform = "translateY(-100%)";
}
anime({
targets: rootWrap,
translateY: direction === "down" ? "-100%" : "0",
duration: 300,
easing: "linear",
begin: start,
complete: () => {
rootWrap.removeChild(canvas);
rootWrap.style.transform = "translateY(0%)";
isScrolling.value = false;
},
});
});
};
const isScrolling = ref(false);
const handleScroll = async (ev) => {
let direction = ev.deltaY > 0 ? "down" : "up";
// 判断滚轮滚动方向
if (
(direction === "down" && mapIndex.value >= methods.length - 1) ||
(direction === "up" && mapIndex.value <= 0)
) {
return;
}
ev.preventDefault();
ev.stopPropagation();
if (isScrolling.value) {
return;
}
isScrolling.value = true;
playScrollAnimation(direction, () => {});
if (direction === "down" && mapIndex.value < methods.length - 1) {
emit("changeMapIndex", mapIndex.value + 1);
} else if (direction === "up" && mapIndex.value > 0) {
emit("changeMapIndex", mapIndex.value - 1);
}
};
watch(mapIndex, (newVal, oldVal) => {
console.log(`mapIndex change from ${oldVal} to ${newVal}`);
if (newVal === oldVal) {
return;
}
isScrolling.value = true;
const direction = newVal > oldVal ? "down" : "up";
playScrollAnimation(direction, () => {});
if (locale.value === "zh") {
loadChinaDistrict("100000");
} else {
loadRussiaDistrict();
}
});
watch(locale, (newVal) => {
console.log(newVal);
if (newVal === "zh") {
loadChinaDistrict("100000");
} else if (newVal === "ru") {
loadRussiaDistrict();
}
});
</script>
<template>
<div class="root-wrap">
<div class="root-container">
<div class="page-wrap" @wheel="handleScroll">
<div class="title">{{ methods[mapIndex].title.value }}</div>
<div v-if="loading" class="loading-modal"></div>
<!-- 返回上一级按钮 -->
<div class="back-btn" @click="backMap">
<img :src="backBtnPng" alt="back" />
</div>
<!-- 人数表格 -->
<div class="count-table area">
<div class="table">
<div class="head">
<div class="th">
<div class="title">地区</div>
<div class="count">人数</div>
</div>
</div>
<div class="body">
<div v-for="item in areaCountPaged" :key="item.adcode" class="tr">
<div class="title">{{ item.name }}</div>
<div class="count">{{ item.count }}</div>
</div>
</div>
</div>
<RegionPagine
v-model:page="leftBoxPageNum"
:total="areaCount.length"
/>
</div>
<!-- 领域 -->
<div class="count-table industry">
<div class="table">
<div class="head">
<div class="th">
<div class="title">领域</div>
<div class="count">人数</div>
</div>
</div>
<div class="body">
<div
v-for="item in industryCountPaged"
:key="item.adcode"
class="tr"
>
<div class="title">{{ item.name }}</div>
<div class="count">{{ item.count }}</div>
</div>
</div>
</div>
<RegionPagine
v-model:page="rightBoxPageNum"
:total="industryCount.length"
/>
</div>
<div id="map-container" ref="mapRef"></div>
<div
id="container"
style="
width: 100%;
height: 100%;
background-color: rgb(0, 0, 0);
display: none;
"
></div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.scroll-to-top {
animation: 1s linear infinite page-scroll;
}
.root-wrap {
width: 100%;
height: 100%;
overflow: hidden;
.root-container {
width: 100%;
height: 100%;
> canvas {
transition: all 0.3s linear;
}
.page-wrap {
width: 100%;
height: 100%;
//top: 0;
//left: 0;
position: relative;
//transition: all 0.3s linear;
//animation: 1s linear forwards page-scroll;
.loading-modal {
position: absolute;
left: 0;
top: 0;
background: #f0f2f5;
}
#map-container {
width: 100%;
height: 100%;
background: linear-gradient(0deg, #010101, #041744);
}
> .title {
position: absolute;
top: 75px;
width: 100%;
text-align: center;
font-size: 36px;
font-family: Source Han Sans CN, sans-serif;
font-weight: 300;
color: #ffffff;
z-index: 101;
}
.back-btn {
position: absolute;
left: 120px;
top: 120px;
z-index: 99;
cursor: pointer;
}
.count-table {
position: absolute;
z-index: 101;
&.area {
top: 200px;
left: 120px;
}
&.industry {
top: 200px;
right: 120px;
}
.table {
border: 1px solid #0054ff;
.head,
.body {
.tr,
.th {
display: flex;
color: white;
.title,
.count {
width: 100px;
padding: 8px 12px;
text-align: center;
}
.title {
border-right: 1px solid #0054ff;
}
}
.th {
border-bottom: 1px solid #0054ff;
}
.tr {
color: rgb(161, 192, 255);
}
}
}
.pagination {
display: flex;
}
}
}
}
}
//@keyframes page-scroll {
// 0% {
// transform: translateY(0);
// }
// 100% {
// transform: translateY(-100%);
// }
//}
</style>

View File

@ -1,32 +1,34 @@
<template>
<div v-loading="loading" class="fullPage" ref="fullPageRef">
<div ref="fullPageRef" v-loading="loading" class="fullPage">
<div class="indicator">
<div
:class="`${state.fullpage.current == idx ? 'active' : ''}`"
v-for="idx in len"
:key="idx"
v-for="idx in 6"
:id="idx.toString()"
:key="idx"
:class="`${indicatorActiveIndex == idx ? 'active' : ''}`"
class="point"
@click="handleIndicatorClick(idx)"
></div>
</div>
<div
class="fullPageContainer"
:data-index="state.fullpage.current"
ref="fullPageContainerRef"
:data-index="state.fullpage.current"
class="fullPageContainer"
@mousewheel="mouseWheelHandle"
>
<!-- @DOMMouseScroll="mouseWheelHandle" -->
<div
v-for="(item, $index) in state.boxList"
class="section"
:key="$index"
:style="`z-index: ${item.zIndex};`"
class="section"
>
<!-- v-if="`index${state.fullpage.current}` == item.title" -->
<component
v-if="Math.abs(state.fullpage.current - ($index + 1)) <= 1"
:is="item.comp"
v-if="Math.abs(state.fullpage.current - ($index + 1)) <= 1"
:map-index="mapIndex"
@changeMapIndex="mapIndex = $event"
></component>
</div>
</div>
@ -50,21 +52,9 @@
</template>
<script setup>
import {
reactive,
onMounted,
ref,
shallowRef,
onUnmounted,
computed,
} from "vue";
import { computed, onMounted, reactive, ref, shallowRef } from "vue";
import index0 from "./comp/index0.vue";
import index1 from "./comp/index1.vue";
import index2 from "./comp/index2.vue";
import index3 from "./comp/index3.vue";
import index4 from "./comp/index4.vue";
import index5 from "./comp/index5.vue";
import index6 from "./comp/index6.vue";
import index7 from "./comp/index7.vue";
import index8 from "./comp/index8.vue";
const loading = ref(true);
@ -73,6 +63,8 @@ setTimeout(() => {
}, 500);
const fullPageRef = ref();
const fullPageContainerRef = ref();
const mapIndex = ref(0); // 0-3
const mapRef = ref();
onMounted(() => {
fullPageContainerRef.value.addEventListener(
"DOMMouseScroll",
@ -88,6 +80,7 @@ onMounted(() => {
// });
const len = computed(() => state.boxList.length);
// const len = ref(6);
const next = () => {
// 往下切换
// TODO:
@ -106,6 +99,7 @@ const pre = () => {
move(state.fullpage.current); // 执行切换
}
};
function move(index) {
state.fullpage.isScrolling = true; // 为了防止滚动多页,需要通过一个变量来控制是否滚动
directToMove(index); //执行滚动
@ -114,6 +108,7 @@ function move(index) {
state.fullpage.isScrolling = false;
}, 1010);
}
function directToMove(index) {
let height = fullPageRef.value["clientHeight"]; //获取屏幕的宽度
// let scrollPage = proxy.$refs["fullPageContainer"]; // 获取执行tarnsform的元素
@ -122,6 +117,7 @@ function directToMove(index) {
fullPageContainerRef.value.style.transform = `translateY(${scrollHeight})`;
state.fullpage.current = index;
}
function mouseWheelHandle(event) {
// 监听鼠标监听
// 添加冒泡阻止
@ -157,35 +153,10 @@ let state = reactive({
zIndex: 1,
title: "index1",
},
// {
// comp: shallowRef(index2),
// zIndex: 1,
// title: "index2",
// },
{
comp: shallowRef(index3),
comp: shallowRef(index0),
zIndex: 1,
title: "index3",
},
// {
// comp: shallowRef(index4),
// zIndex: 1,
// title: "index4",
// },
{
comp: shallowRef(index5),
zIndex: 1,
title: "index5",
},
{
comp: shallowRef(index6),
zIndex: 1,
title: "index6",
},
{
comp: shallowRef(index7),
zIndex: 1,
title: "index7",
title: "index1",
},
{
comp: shallowRef(index8),
@ -199,19 +170,46 @@ let state = reactive({
},
});
const indicatorActiveIndex = computed(() => {
if (state.fullpage.current === 1) {
return 1;
} else if (state.fullpage.current === 2) {
return mapIndex.value + 2;
} else {
return state.fullpage.current + 3;
}
});
// 点击 indicator 时
const handleIndicatorClick = (idx) => {
if (idx == state.fullpage.current) {
return;
} else if (idx > state.fullpage.current) {
for (let i = state.fullpage.current; i < idx; i++) {
console.log(idx);
let boxIndex;
let _mapIndex;
if (idx === 1) {
boxIndex = 1;
_mapIndex = null;
} else if (idx <= 5) {
boxIndex = 2;
_mapIndex = idx - 2;
} else {
boxIndex = 3;
_mapIndex = null;
}
if (boxIndex > state.fullpage.current) {
for (let i = state.fullpage.current; i < boxIndex; i++) {
next();
}
} else {
for (let i = state.fullpage.current; i > idx; i--) {
} else if (boxIndex < state.fullpage.current) {
for (let i = state.fullpage.current; i > boxIndex; i--) {
pre();
}
}
if (_mapIndex !== null && _mapIndex !== mapIndex.value) {
// mapRef.value?.loadMapByIndex(_mapIndex)
// console.log(mapRef.value.loadMapByIndex)
mapIndex.value = _mapIndex;
}
};
</script>
@ -221,32 +219,38 @@ const handleIndicatorClick = (idx) => {
height: 100%;
overflow: hidden;
position: relative;
.indicator {
position: absolute;
top: 50%;
right: 20px;
z-index: 999;
transform: translateY(-50%);
.point {
transition: all 1s;
width: 12px;
height: 12px;
background-color: #999;
border-radius: 50%;
&.active {
background-color: #0054ff;
}
&:not(:last-child) {
margin-bottom: 6px;
}
}
}
}
.fullPageContainer {
width: 100%;
height: 100%;
transition: all linear 0.5s;
}
.section {
width: 100%;
height: 100%;

View File

@ -143,17 +143,14 @@
</template>
<script setup>
import Cookies from "js-cookie";
// import { encrypt, decrypt } from "@/utils/jsencrypt";
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";
import { useRouter } from "vue-router";
import { useI18n } from "vue-i18n";
import { computed, reactive } from "vue";
import { reactify } from "@vueuse/core";
import { computed, reactive, ref } from "vue"; // const store = useStore();
// const store = useStore();
const router = useRouter();
@ -176,9 +173,9 @@ const registerForm = ref({
const loginRef = ref();
const isCheckRef = ref();
const disabled = ref(true);
const buttonName = ref("获取验证码");
const isDisabled = ref(false);
const time = ref(10);
// const buttonName = ref("获取验证码");
// const isDisabled = ref(false);
// const time = ref(10);
const loginRules = reactive({
username: [
@ -199,7 +196,7 @@ const loginRules = reactive({
const isCheck = (rule, value, callback) => {
if (!value) {
callback(new Error("请阅读并勾选"));
callback(new Error(t("register.readAndCheck")));
} else {
callback();
}

View File

@ -4,13 +4,26 @@
<div v-if="data.image" class="img">
<el-image :src="data.image" alt="" style="height: 150px" />
</div>
<div v-else-if="data.type == 1" class="type">产品</div>
<div v-else-if="data.type == 2" class="type">成果</div>
<div v-else-if="data.type == 1" class="type">
<!--产品-->
{{ t("admin.form.product") }}
</div>
<div v-else-if="data.type == 2" class="type">
{{ t("admin.common.achievement") }}
</div>
<div class="content">
<div class="tit" @click="handleDetail(data.dataId)">
<!-- {{-->
<!-- data.type == 2 ? `成果名称 `: data.type == 1 ? "产品名称:" : ""-->
<!-- }}-->
{{
data.type == 2 ? "成果名称" : data.type == 1 ? "产品名称" : ""
}}
t("admin.form.name", {
type:
data.type == 2
? t("admin.common.achievement")
: t("admin.form.product"),
})
}}
{{ data.title }}
</div>
<slot name="des" />
@ -31,11 +44,12 @@
}}</span>
</div> -->
<div class="line">
联系人
<!-- 联系人-->
{{ t("webSearch.contact") }}
<span>中科云平台</span>
</div>
<div class="line">
联系方式(微信同号)
{{ t("webSearch.contactInformation") }}({{ t("webSearch.wechat") }})
<span>18156053255</span>
</div>
</div>
@ -66,6 +80,7 @@ import { useRoute, useRouter } from "vue-router";
import { toRefs } from "vue";
import { addViewHistory } from "@/utils/view_history";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const route = useRoute();

View File

@ -16,7 +16,7 @@
<span>{{ data.industryStr }}</span>
</div>
<div class="line">
{{ t("webSearch.webSite") }}
{{ t("admin.form.labWebsite") }}
<span>{{ data.url }}</span>
</div>
<div class="line">

View File

@ -40,7 +40,7 @@
:disabled="!state.currentKeyword"
icon="Search"
@click="handleQuery"
>搜索
>{{ t("common.search") }}
</el-button>
</template>
</el-input>

View File

@ -64,7 +64,9 @@
</div>
</section>
<div style="padding: 20px 0">
<div class="pointTit">毕业院校</div>
<div class="pointTit">
{{ t("admin.table.graduateSchool") }}
</div>
</div>
<section>
<div>

View File

@ -50,7 +50,7 @@
</div>
<div class="line">
<!-- 网址-->
{{ t("webSearch.website") }}
{{ t("admin.form.labWebsite") }}
<a :href="state.LabDetail.url" target="_blank">{{
state.LabDetail.url