lizhong 3 years ago
commit b9b4fa7473

@ -0,0 +1,19 @@
root = true
[*]
charset=utf-8
end_of_line=lf
insert_final_newline=true
indent_style=space
indent_size=2
max_line_length = 100
[*.{yml,yaml,json}]
indent_style = space
indent_size = 2
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab

@ -0,0 +1,35 @@
# Whether to open mock
VITE_USE_MOCK = true
# public path
VITE_PUBLIC_PATH = /
# Delete console
VITE_DROP_CONSOLE = true
# Whether to enable gzip or brotli compression
# Optional: gzip | brotli | none
# If you need multiple forms, you can use `,` to separate
VITE_BUILD_COMPRESS = 'none'
# Whether to delete origin files when using compress, default false
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
# Basic interface address SPA
VITE_GLOB_API_URL=/basic-api
# File upload address optional
# It can be forwarded by nginx or write the actual address directly
VITE_GLOB_UPLOAD_URL=/upload
# Interface prefix
VITE_GLOB_API_URL_PREFIX=
# Whether to enable image compression
VITE_USE_IMAGEMIN= true
# use pwa
VITE_USE_PWA = false
# Is it compatible with older browsers
VITE_LEGACY = false

@ -0,0 +1,36 @@
NODE_ENV=production
# Whether to open mock
VITE_USE_MOCK = true
# public path
VITE_PUBLIC_PATH = /
# Delete console
VITE_DROP_CONSOLE = true
# Whether to enable gzip or brotli compression
# Optional: gzip | brotli | none
# If you need multiple forms, you can use `,` to separate
VITE_BUILD_COMPRESS = 'none'
# Whether to delete origin files when using compress, default false
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
# Basic interface address SPA
VITE_GLOB_API_URL=/basic-api
# File upload address optional
# It can be forwarded by nginx or write the actual address directly
VITE_GLOB_UPLOAD_URL=/upload
# Interface prefix
VITE_GLOB_API_URL_PREFIX=
# Whether to enable image compression
VITE_USE_IMAGEMIN= true
# use pwa
VITE_USE_PWA = false
# Is it compatible with older browsers
VITE_LEGACY = false

11
.gitignore vendored

@ -0,0 +1,11 @@
node_modules
*.log*
.nuxt
.nitro
.cache
.output
.env
.env.local
.env.development
dist
.DS_Store

8
.idea/.gitignore vendored

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/reptileFront_nuxt.iml" filepath="$PROJECT_DIR$/.idea/reptileFront_nuxt.iml" />
</modules>
</component>
</project>

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/main" vcs="Git" />
</component>
</project>

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectTasksOptions" suppressed-tasks="Less;SCSS" />
</project>

@ -0,0 +1,2 @@
shamefully-hoist=true
strict-peer-dependencies=false

@ -0,0 +1,51 @@
# Nuxt 3 Minimal Starter
Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
## Setup
Make sure to install the dependencies:
```bash
# yarn
yarn install
# npm
npm install
# pnpm
pnpm install
```
## Development Server
Start the development server on http://localhost:3000
```bash
npm run dev
```
## Production
Build the application for production:
```bash
npm run build
```
Locally preview production build:
```bash
npm run preview
```
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
## 项目相关资料
[后台地址](http://research.mcnetmart.com/NEBnKkdLDW.php)
账号admin
密码a147258"
[原型图](https://eqbh1l.axshare.com)
[设计稿](https://lanhuapp.com/web/#/item/project/stage?tid=279a1988-b0b6-4aac-9ef2-c066ece4ddcf&pid=6cb69861-790d-4f5b-9e80-8c059a873c9a
)

@ -0,0 +1,15 @@
import Http from '~~/utils/http';
import {ResOptions} from "~~/api/types";
export default new class AboutApi extends Http<ResOptions<any>> {
private readonly prefix = '/index'
private readonly about_url = '/aboutUs'
private readonly contract_url = '/contactUs'
getAboutInfo() {
return this.get(this.contact(this.prefix, this.about_url))
}
getContractInfo() {
return this.get(this.contact(this.prefix, this.contract_url))
}
}

@ -0,0 +1,15 @@
import Http from '~~/utils/http';
import {PaginationVo, ResOptions} from "~~/api/types";
export default new class Case extends Http<ResOptions<any>> {
private readonly caseList = '/cases/lists'
private readonly caseInfo = '/cases/info'
getCaseList(params: PaginationVo) {
return this.get(this.caseList, params)
}
getCaseInfo(id: number) {
return this.get(this.caseInfo, {id})
}
}

@ -0,0 +1,16 @@
import Http from '~~/utils/http';
import {ResOptions} from "~~/api/types";
/**
* @description
* */
export default new class Comment extends Http<ResOptions<any>> {
private readonly submitComment_url = '/comment/submitComment'
private readonly submitConsult_url = '/comment/submitConsult'
submitComment(data: any) {
return this.post(this.submitComment_url, this.toFormData(data))
}
submitConsult(data: any) {
return this.post(this.submitConsult_url, this.toFormData(data))
}
}

@ -0,0 +1,21 @@
import Http from '~~/utils/http';
import {PaginationVo, ResOptions} from "~~/api/types";
export default new class Common extends Http<ResOptions<any>> {
private readonly upload = '/common/upload'
private readonly caseList = '/cases/lists'
private readonly bottomMenu = '/index/bottomMenu'
handleUpload(file: any) {
const formData = new FormData();
formData.append('file', file)
return this.post(this.upload, formData)
}
getCaseList(params: PaginationVo) {
return this.get(this.caseList, params)
}
getBottomMenu() {
return this.get(this.bottomMenu)
}
}

@ -0,0 +1,27 @@
import Http from '~~/utils/http';
import {InformationDto, PaginationDto, ResOptions} from "~~/api/types";
export default new class AboutApi extends Http<ResOptions<PaginationDto<InformationDto>>> {
private readonly prefix = '/index'
private readonly hotNews = '/hotInformation'
private readonly hotTechnical = '/hotTechnical'
private readonly hotCase = '/hotCase'
private readonly hotTs = '/hotTs'
getHotNewsInfo(page: number, limit: number) {
return this.get(this.contact(this.prefix, this.hotNews), {page, limit})
}
getHotTechnical(page: number, limit: number) {
return this.get(this.contact(this.prefix, this.hotTechnical), {page, limit})
}
getHotCase(page: number, limit: number) {
return this.get(this.contact(this.prefix, this.hotCase), {page, limit})
}
getHotTs(page: number, limit: number) {
return this.get(this.contact(this.prefix, this.hotTs), {page, limit})
}
}

@ -0,0 +1,19 @@
import Http from '~~/utils/http';
import {InformationDto, PaginationDto, ResOptions} from "~~/api/types";
export default new class InformationApi extends Http<ResOptions<any>> {
private readonly infomation = '/infomation/info'
private readonly infomationCate = '/infomation/cate'
private readonly infomationLists = '/infomation/lists'
getInformationDetail(id:string) {
return this.get(this.infomation, {id})
}
getInformationCate() {
return this.get(this.infomationCate)
}
getInformationList(data:any) {
return this.get(this.infomationLists, data)
}
}

@ -0,0 +1,21 @@
import Http from '~~/utils/http';
import {PaginationVo, ResOptions} from "~~/api/types";
export default new class Case extends Http<ResOptions<any>> {
private readonly types = '/ts/cate'
private readonly tsList = '/ts/lists'
private readonly tsInfo = '/ts/info'
getTsTbTypes() {
return this.get(this.types)
}
getTsList(params:any) {
return this.get(this.tsList, params)
}
getTsInfo(id:any) {
return this.get(this.tsInfo, {id})
}
}

@ -0,0 +1,19 @@
import Http from '~~/utils/http';
import {InformationDto, PaginationDto, ResOptions} from "~~/api/types";
export default new class TechnicalApi extends Http<ResOptions<any>> {
private readonly technical = '/technical/info'
private readonly technicalCate = '/technical/cate'
private readonly technicalLists = '/technical/lists'
getTechnicalDetail(id:string) {
return this.get(this.technical, {id})
}
getTechnicalCate() {
return this.get(this.technicalCate)
}
getTechnicalList(data:any) {
return this.get(this.technicalLists, data)
}
}

@ -0,0 +1,19 @@
import Http from '~~/utils/http';
import {InformationDto, PaginationDto, ResOptions} from "~~/api/types";
export default new class WarningApi extends Http<ResOptions<any>> {
private readonly warning = '/warning/info'
private readonly warningCate = '/warning/cate'
private readonly warningLists = '/warning/lists'
getWarningDetail(id:string) {
return this.get(this.warning, {id})
}
getWarningCate() {
return this.get(this.warningCate)
}
getWarningList(data:any) {
return this.get(this.warningLists, data)
}
}

@ -0,0 +1,21 @@
import aboutApi from "~~/api/About";
import homeApi from "~~/api/Home";
import commonApi from "~~/api/Common";
import commentApi from "~~/api/Comment";
import informationApi from "~~/api/Information";
import warningApi from "~~/api/Warning";
import technicalApi from "~~/api/Technical";
import tbspApi from "~~/api/TbSp";
import caseApi from "~~/api/Case";
export default {
aboutApi,
homeApi,
commonApi,
commentApi,
informationApi,
warningApi,
technicalApi,
tbspApi,
caseApi,
}

@ -0,0 +1,32 @@
// 后端返回的数据类型
export interface ResOptions<t> {
data?: t
code?: number
msg?: string
}
export interface PaginationDto<T> {
data?: T;
total?: number;
current_page?: number;
last_page?: number;
per_page?: number;
}
export interface PaginationVo {
page?: number;
limit?: number;
keyword?: string;
}
export interface InformationDto {
title: string;
id: any;
description?: string;
category_id?: any;
updatetime?: any;
createtime?: any;
image?: any;
}

@ -0,0 +1,45 @@
<template>
<div>
<el-config-provider>
<NuxtLayout :name="layout">
<NuxtPage></NuxtPage>
</NuxtLayout>
</el-config-provider>
<!-- AppConf-->
</div>
</template>
<script setup lang="ts">
// import TabLayout from "~/layouts/TabLayout.vue";
const layout = 'tab-layout'
</script>
<style lang="scss">
$prefix-cls: '#{$name-prefix}-mai-wrap';
.#{$prefix-cls} {
padding: 110px 120px;
&-content {
margin-top: -60px;
margin-left: 50px;
width: calc(100% - 300px);
h2 {
font-size: 36px;
line-height: 50px;
font-weight: 600;
margin-bottom: 47px;
}
}
&.full{
padding: 110px 290px;
.#{$prefix-cls}-content{
width: 100%;
margin: 0;
}
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 MiB

@ -0,0 +1,138 @@
html {
// header
--header-bg-color: #394664;
--header-bg-hover-color: #273352;
--header-active-menu-bg-color: #273352;
// sider
--sider-dark-bg-color: #273352;
--sider-dark-darken-bg-color: #273352;
--sider-dark-lighten-bg-color: #273352;
}
$white: #fff;
$content-bg: #f4f7f9;
// :export {
// name: "less";
// mainColor: $mainColor;
// fontSize: $fontSize;
// }
$iconify-bg-color: #5551;
// =================================
// ==============border-color=======
// =================================
// Dark-dark
$border-color-dark: #b6b7b9;
// Dark-light
$border-color-shallow-dark: #cececd;
// Light-dark
//$border-color-light: $border-color-base;
// =================================
// ==============message==============
// =================================
// success-bg-color
$success-background-color: #f1f9ec;
// info-bg-color
$info-background-color: #e8eff8;
// warn-bg-color
$warning-background-color: #fdf6ed;
// danger-bg-color
$danger-background-color: #fef0f0;
// =================================
// ==============Header=============
// =================================
$header-dark-bg-color: var(--header-bg-color);
$header-dark-bg-hover-color: var(--header-bg-hover-color);
$header-light-bg-hover-color: #f6f6f6;
$header-light-desc-color: #7c8087;
$header-light-bottom-border-color: #eee;
// top-menu
$top-menu-active-bg-color: var(--header-active-menu-bg-color);
// =================================
// ==============Menu============
// =================================
// let -menu
$sider-dark-bg-color: var(--sider-dark-bg-color);
$sider-dark-darken-bg-color: var(--sider-dark-darken-bg-color);
$sider-dark-lighten-bg-color: var(--sider-dark-lighten-bg-color);
// trigger
$trigger-dark-hover-bg-color: rgba(255, 255, 255, 0.2);
$trigger-dark-bg-color: rgba(255, 255, 255, 0.1);
// =================================
// ==============tree============
// =================================
// tree item hover background
$tree-hover-background-color: #f5f7fa;
// tree item hover font color
$tree-hover-font-color: #f5f7fa;
// =================================
// ==============link============
// =================================
//$link-hover-color: $primary-color;
//$link-active-color: darken($primary-color, 10%);
// =================================
// ==============Text color-=============
// =================================
// Main text color
//$text-color-base: $text-color;
// Label color
$text-color-call-out: #606266;
// Auxiliary information color-dark
$text-color-help-dark: #909399;
// =================================
// ==============breadcrumb=========
// =================================
$breadcrumb-item-normal-color: #999;
// =================================
// ==============button=============
// =================================
//$button-primary-color: $primary-color;
//$button-primary-hover-color: lighten($primary-color, 5%);
//$button-primary-active-color: darken($primary-color, 5%);
$button-ghost-color: $white;
$button-ghost-hover-color: lighten($white, 10%);
$button-ghost-hover-bg-color: #e1ebf6;
$button-ghost-active-color: darken($white, 10%);
//$button-success-color: $success-color;
//$button-success-hover-color: lighten($success-color, 10%);
//$button-success-active-color: darken($success-color, 10%);
//$button-warn-color: $warning-color;
//$button-warn-hover-color: lighten($warning-color, 10%);
//$button-warn-active-color: darken($warning-color, 10%);
//$button-error-color: $error-color;
//$button-error-hover-color: lighten($error-color, 10%);
//$button-error-active-color: darken($error-color, 10%);
//$button-cancel-color: $text-color-call-out;
$button-cancel-bg-color: $white;
//$button-cancel-border-color: $border-color-shallow-dark;
// Mouse over
//$button-cancel-hover-color: $primary-color;
$button-cancel-hover-bg-color: $white;
//$button-cancel-hover-border-color: $primary-color;

@ -0,0 +1,3 @@
@import "color";
@import "public";
@import 'var/index';

@ -0,0 +1,252 @@
$newsPrefix: '#{$name-prefix}-news-wrap';
$informPrefix: '#{$name-prefix}-inform-wrap';
$casePrefix: '#{$name-prefix}-case-wrap';
$rulePrefix: '#{$name-prefix}-rule-wrap';
$hover-color: #14417a;
.mai-ocr-wrap {
width: 100%;
display: flex;
justify-content: center;
padding-top: 60px;
padding-bottom: 25px;
.mai-ocr-item {
padding-left: 110px;
box-sizing: border-box;
align-items: center;
width: 465px;
height: 133px;
font-size: 32px;
font-weight: 400;
color: #022950;
line-height: 45px;
border-radius: 8px;
&:last-child {
background: url('../images/home/shibie.png') center center/100% 100% no-repeat;
margin-left: 270px;
}
&:first-child {
background: url('../images/home/aiocrbg.png') center center/100% 100% no-repeat;
}
}
}
.mai-section {
width: 100%;
padding: 63px 114px;
text-align: center;
.date {
color: #c1c1c1;
text-align: left;
.month {
display: block;
font-size: 28px;
line-height: 32px;
}
.year {
font-size: 20px;
line-height: 24px;
}
}
&.#{$newsPrefix} {
.home-news {
margin-bottom: 82px;
}
.media-img {
width: 785px;
height: 521px;
border-radius: 8px;
overflow: hidden;
margin-right: 78px;
img {
width: 100%;
height: 100%;
}
}
.newsList {
display: flex;
flex-direction: column;
justify-content: space-between;
width: calc(100% - 785px - 78px);
}
.open-new-btn {
width: 44px;
height: 44px;
cursor: pointer;
}
.new-date {
color: #c1c1c1;
.month {
display: block;
font-size: 28px;
line-height: 32px;
}
.year {
font-size: 20px;
line-height: 24px;
}
}
.divider {
height: 64px;
margin: 0 32px;
}
.c-item {
width: calc(100% - 80px);
}
.new-title {
text-align: left;
font-size: 24px;
cursor: pointer;
width: 80%;
white-space: nowrap;
overflow: hidden;
flex-shrink: 0;
&:hover {
color: $hover-color;
font-weight: 500;
}
}
}
&.#{$informPrefix} {
background: url('../images/home/sectionBg.png') center center/100% 100% no-repeat;
.home-inform {
margin-bottom: 43px;
}
.inform-item {
color: #333;
text-align: left;
height: 224px;
padding: 33px 33px 0;
box-sizing: border-box;
border: 1px solid #d2d2d2;
width: calc(100% / 3 - 20px);
margin-bottom: 20px;
background-color: $white;
&:nth-child(3n - 1) {
margin: 0 20px 20px;
}
h3 {
font-weight: 600;
font-size: 24px;
line-height: 34px;
margin-bottom: 33px;
}
p {
font-size: 20px;
line-height: 28px;
word-break: break-word;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 24px;
color: #333;
}
span {
font-size: 18px;
color: #666;
}
&:hover {
background-color: $hover-color;
box-shadow: 0 0 13px 3px rgb(134 134 134 / 51%);
* {
color: $white;
}
}
}
}
&.#{$rulePrefix} {
.home-rule {
.rule-item {
width: calc((100% - 22px) / 2);
height: 110px;
margin-bottom: 22px;
border: 1px solid #d2d2d2;
box-sizing: border-box;
padding-left: 30px;
.title {
font-size: 24px;
font-weight: 500;
//color: #333;
margin-left: 40px;
}
&:hover {
background-color: $hover-color;
* {
color: $white !important;
}
}
}
}
}
&.#{$casePrefix} {
background-color: #f6f7fb;
.home-case {
margin-bottom: 40px;
.case-item {
text-align: left;
width: calc(100% / 3 - 20px);
&:nth-child(3n-1) {
margin: 0 20px 20px;
}
.img {
//width: 100%px;
height: 447px;
border-radius: 16px;
margin-bottom: 16px;
}
p {
font-weight: 400;
line-height: 28px;
font-size: 20px;
margin-bottom: 30px;
}
.more {
width: 109px;
height: 37px;
border-radius: 19px;
border: 1px solid #022950;
font-size: 20px;
font-weight: 400;
line-height: 28px;
}
}
}
}
}

@ -0,0 +1,54 @@
#__nuxt {
width: 100%;
height: 100%;
min-width: 1800px;
margin: 0 auto;
max-width: 1940px;
}
// =================================
// ==============scrollbar==========
// =================================
::-webkit-scrollbar {
width: 7px;
height: 8px;
}
// ::-webkit-scrollbar-track {
// background: transparent;
// }
::-webkit-scrollbar-track {
background-color: rgb(0 0 0 / 5%);
}
::-webkit-scrollbar-thumb {
// background: rgba(0, 0, 0, 0.6);
background-color: rgb(144 147 153 / 30%);
// background-color: rgba(144, 147, 153, 0.3);
border-radius: 2px;
box-shadow: inset 0 0 6px rgb(0 0 0 / 20%);
}
::-webkit-scrollbar-thumb:hover {
background-color: $border-color-dark;
}
// =================================
// ==============nprogress==========
// =================================
#nprogress {
pointer-events: none;
.bar {
position: fixed;
top: 0;
left: 0;
z-index: 99999;
width: 100%;
height: 2px;
//background-color: @primary-color;
opacity: 0.75;
}
}

@ -0,0 +1,33 @@
// =================================
// ==========================
// =================================
// Extra small screen / phone
$screen-xs: 480px;
$screen-xs-min: $screen-xs;
// Small screen / tablet
$screen-sm: 576px;
$screen-sm-min: $screen-sm;
// Medium screen / desktop
$screen-md: 768px;
$screen-md-min: $screen-md;
// Large screen / wide desktop
$screen-lg: 992px;
$screen-lg-min: $screen-lg;
// Extra large screen / full hd
$screen-xl: 1200px;
$screen-xl-min: $screen-xl;
// Extra extra large screen / large desktop
$screen-2xl: 1600px;
$screen-2xl-min: $screen-2xl;
$screen-xs-max: ($screen-sm-min - 1px);
$screen-sm-max: ($screen-md-min - 1px);
$screen-md-max: ($screen-lg-min - 1px);
$screen-lg-max: ($screen-xl-min - 1px);
$screen-xl-max: ($screen-2xl-min - 1px);

@ -0,0 +1,18 @@
// =================================
// ==============-===========
// =================================
$ease-base-out: cubic-bezier(0.7, 0.3, 0.1, 1);
$ease-base-in: cubic-bezier(0.9, 0, 0.3, 0.7);
$ease-out: cubic-bezier(0.215, 0.61, 0.355, 1);
$ease-in: cubic-bezier(0.55, 0.055, 0.675, 0.19);
$ease-in-out: cubic-bezier(0.645, 0.045, 0.355, 1);
$ease-out-back: cubic-bezier(0.12, 0.4, 0.29, 1.46);
$ease-in-back: cubic-bezier(0.71, -0.46, 0.88, 0.6);
$ease-in-out-back: cubic-bezier(0.71, -0.46, 0.29, 1.46);
$ease-out-circ: cubic-bezier(0.08, 0.82, 0.17, 1);
$ease-in-circ: cubic-bezier(0.6, 0.04, 0.98, 0.34);
$ease-in-out-circ: cubic-bezier(0.78, 0.14, 0.15, 0.86);
$ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1);
$ease-in-quint: cubic-bezier(0.755, 0.05, 0.855, 0.06);
$ease-in-out-quint: cubic-bezier(0.86, 0, 0.07, 1);

@ -0,0 +1,32 @@
//$import (reference) '../color.less';
@import 'easing';
@import 'breakpoint';
$name-prefix: 'yangliu';
// tabs
$multiple-height: 30px;
// headers
$header-height: 100px;
// logo width
$logo-width: 32px;
//
$side-drag-z-index: 200;
$page-loading-z-index: 10000;
$lock-page-z-index: 3000;
$layout-header-fixed-z-index: 500;
$multiple-tab-fixed-z-index: 505;
$layout-sider-fixed-z-index: 510;
$layout-mix-sider-fixed-z-index: 550;
$preview-comp-z-index: 1000;
$page-footer-z-index: 99;

@ -0,0 +1,419 @@
tinymce.addI18n('es', {
Redo: 'Rehacer',
Undo: 'Deshacer',
Cut: 'Cortar',
Copy: 'Copiar',
Paste: 'Pegar',
'Select all': 'Seleccionar todo',
'New document': 'Nuevo documento',
Ok: 'Ok',
Cancel: 'Cancelar',
'Visual aids': 'Ayudas visuales',
Bold: 'Negrita',
Italic: 'Cursiva',
Underline: 'Subrayado',
Strikethrough: 'Tachado',
Superscript: 'Super\u00edndice',
Subscript: 'Sub\u00edndice',
'Clear formatting': 'Limpiar formato',
'Align left': 'Alinear a la izquierda',
'Align center': 'Alinear al centro',
'Align right': 'Alinear a la derecha',
Justify: 'Justificar',
'Bullet list': 'Lista de vi\u00f1etas',
'Numbered list': 'Lista numerada',
'Decrease indent': 'Disminuir sangr\u00eda',
'Increase indent': 'Incrementar sangr\u00eda',
Close: 'Cerrar',
Formats: 'Formatos',
"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": 'Su navegador no es compatible con el acceso directo al portapapeles. Use las teclas Crtl+X\/C\/V de su teclado.',
Headers: 'Encabezados',
'Header 1': 'Encabezado 1',
'Header 2': 'Encabezado 2',
'Header 3': 'Encabezado 3',
'Header 4': 'Encabezado 4',
'Header 5': 'Encabezado 5',
'Header 6': 'Encabezado 6',
Headings: 'Encabezados',
'Heading 1': 'Encabezado 1',
'Heading 2': 'Encabezado 2',
'Heading 3': 'Encabezado 3',
'Heading 4': 'Encabezado 4',
'Heading 5': 'Encabezado 5',
'Heading 6': 'Encabezado 6',
Preformatted: 'Con formato previo',
Div: 'Div',
Pre: 'Pre',
Code: 'C\u00f3digo',
Paragraph: 'P\u00e1rrafo',
Blockquote: 'Blockquote',
Inline: 'Alineado',
Blocks: 'Bloques',
'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.': 'Pegar est\u00e1 ahora en modo de texto plano. El contenido se pegar\u00e1 como texto plano hasta que desactive esta opci\u00f3n.',
Fonts: 'Fuentes',
'Font Sizes': 'Tama\u00f1os de fuente',
Class: 'Clase',
'Browse for an image': 'Buscar una imagen',
OR: 'OR',
'Drop an image here': 'Arrastre una imagen aqu\u00ed',
Upload: 'Cargar',
Block: 'Bloque',
Align: 'Alinear',
Default: 'Por defecto',
Circle: 'C\u00edrculo',
Disc: 'Disco',
Square: 'Cuadrado',
'Lower Alpha': 'Inferior Alfa',
'Lower Greek': 'Inferior Griega',
'Lower Roman': 'Inferior Romana',
'Upper Alpha': 'Superior Alfa',
'Upper Roman': 'Superior Romana',
'Anchor...': 'Anclaje...',
Name: 'Nombre',
Id: 'Id',
'Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.': 'Deber\u00eda comenzar por una letra, seguida solo de letras, n\u00fameros, guiones, puntos, dos puntos o guiones bajos.',
'You have unsaved changes are you sure you want to navigate away?': 'Tiene cambios sin guardar. \u00bfEst\u00e1 seguro de que quiere salir?',
'Restore last draft': 'Restaurar el \u00faltimo borrador',
'Special character...': 'Car\u00e1cter especial...',
'Source code': 'C\u00f3digo fuente',
'Insert\/Edit code sample': 'Insertar\/editar c\u00f3digo de prueba',
Language: 'Idioma',
'Code sample...': 'Ejemplo de c\u00f3digo...',
'Color Picker': 'Selector de colores',
R: 'R',
G: 'V',
B: 'A',
'Left to right': 'De izquierda a derecha',
'Right to left': 'De derecha a izquierda',
'Emoticons...': 'Emoticones...',
'Metadata and Document Properties': 'Metadatos y propiedades del documento',
Title: 'T\u00edtulo',
Keywords: 'Palabras clave',
Description: 'Descripci\u00f3n',
Robots: 'Robots',
Author: 'Autor',
Encoding: 'Codificaci\u00f3n',
Fullscreen: 'Pantalla completa',
Action: 'Acci\u00f3n',
Shortcut: 'Atajo',
Help: 'Ayuda',
Address: 'Direcci\u00f3n',
'Focus to menubar': 'Enfocar la barra del men\u00fa',
'Focus to toolbar': 'Enfocar la barra de herramientas',
'Focus to element path': 'Enfocar la ruta del elemento',
'Focus to contextual toolbar': 'Enfocar la barra de herramientas contextual',
'Insert link (if link plugin activated)': 'Insertar enlace (si el complemento de enlace est\u00e1 activado)',
'Save (if save plugin activated)': 'Guardar (si el componente de salvar est\u00e1 activado)',
'Find (if searchreplace plugin activated)': 'Buscar (si el complemento buscar-remplazar est\u00e1 activado)',
'Plugins installed ({0}):': 'Plugins instalados ({0}):',
'Premium plugins:': 'Complementos premium:',
'Learn more...': 'Aprende m\u00e1s...',
'You are using {0}': 'Estas usando {0}',
Plugins: 'Complementos',
'Handy Shortcuts': 'Accesos directos',
'Horizontal line': 'L\u00ednea horizontal',
'Insert\/edit image': 'Insertar\/editar imagen',
'Image description': 'Descripci\u00f3n de la imagen',
Source: 'Enlace',
Dimensions: 'Dimensiones',
'Constrain proportions': 'Restringir proporciones',
General: 'General',
Advanced: 'Avanzado',
Style: 'Estilo',
'Vertical space': 'Espacio vertical',
'Horizontal space': 'Espacio horizontal',
Border: 'Borde',
'Insert image': 'Insertar imagen',
'Image...': 'Imagen...',
'Image list': 'Lista de im\u00e1genes',
'Rotate counterclockwise': 'Girar a la izquierda',
'Rotate clockwise': 'Girar a la derecha',
'Flip vertically': 'Invertir verticalmente',
'Flip horizontally': 'Invertir horizontalmente',
'Edit image': 'Editar imagen',
'Image options': 'Opciones de imagen',
'Zoom in': 'Acercar',
'Zoom out': 'Alejar',
Crop: 'Recortar',
Resize: 'Redimensionar',
Orientation: 'Orientaci\u00f3n',
Brightness: 'Brillo',
Sharpen: 'Forma',
Contrast: 'Contraste',
'Color levels': 'Niveles de color',
Gamma: 'Gamma',
Invert: 'Invertir',
Apply: 'Aplicar',
Back: 'Atr\u00e1s',
'Insert date\/time': 'Insertar fecha\/hora',
'Date\/time': 'Fecha\/hora',
'Insert\/Edit Link': 'Insertar\/editar enlace',
'Insert\/edit link': 'Insertar\/editar enlace',
'Text to display': 'Texto para mostrar',
Url: 'URL',
'Open link in...': 'Abrir enlace en...',
'Current window': 'Ventana actual',
None: 'Ninguno',
'New window': 'Nueva ventana',
'Remove link': 'Quitar enlace',
Anchors: 'Anclas',
'Link...': 'Enlace...',
'Paste or type a link': 'Pega o introduce un enlace',
'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?': 'El enlace que has introducido no parece ser una direcci\u00f3n de correo electr\u00f3nico. Quieres a\u00f1adir el prefijo necesario mailto: ?',
'The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?': 'El enlace que has introducido no parece ser una enlace externo. Quieres a\u00f1adir el prefijo necesario http:\/\/ ?',
'Link list': 'Lista de enlaces',
'Insert video': 'Insertar video',
'Insert\/edit video': 'Insertar\/editar video',
'Insert\/edit media': 'Insertar\/editar medio',
'Alternative source': 'Enlace alternativo',
'Alternative source URL': 'Origen de URL alternativo',
'Media poster (Image URL)': 'P\u00f3ster de medio (URL de imagen)',
'Paste your embed code below:': 'Pega tu c\u00f3digo embebido debajo',
Embed: 'Incrustado',
'Media...': 'Medios...',
'Nonbreaking space': 'Espacio fijo',
'Page break': 'Salto de p\u00e1gina',
'Paste as text': 'Pegar como texto',
Preview: 'Previsualizar',
'Print...': 'Imprimir...',
Save: 'Guardar',
Find: 'Buscar',
'Replace with': 'Reemplazar con',
Replace: 'Reemplazar',
'Replace all': 'Reemplazar todo',
Previous: 'Anterior',
Next: 'Siguiente',
'Find and replace...': 'Buscar y reemplazar...',
'Could not find the specified string.': 'No se encuentra la cadena de texto especificada',
'Match case': 'Coincidencia exacta',
'Find whole words only': 'Solo palabras completas',
'Spell check': 'Revisar ortograf\u00eda',
Ignore: 'Ignorar',
'Ignore all': 'Ignorar todos',
Finish: 'Finalizar',
'Add to Dictionary': 'A\u00f1adir al Diccionario',
'Insert table': 'Insertar tabla',
'Table properties': 'Propiedades de la tabla',
'Delete table': 'Eliminar tabla',
Cell: 'Celda',
Row: 'Fila',
Column: 'Columna',
'Cell properties': 'Propiedades de la celda',
'Merge cells': 'Combinar celdas',
'Split cell': 'Dividir celdas',
'Insert row before': 'Insertar fila antes',
'Insert row after': 'Insertar fila despu\u00e9s ',
'Delete row': 'Eliminar fila',
'Row properties': 'Propiedades de la fila',
'Cut row': 'Cortar fila',
'Copy row': 'Copiar fila',
'Paste row before': 'Pegar la fila antes',
'Paste row after': 'Pegar la fila despu\u00e9s',
'Insert column before': 'Insertar columna antes',
'Insert column after': 'Insertar columna despu\u00e9s',
'Delete column': 'Eliminar columna',
Cols: 'Columnas',
Rows: 'Filas',
Width: 'Ancho',
Height: 'Alto',
'Cell spacing': 'Espacio entre celdas',
'Cell padding': 'Relleno de celda',
'Show caption': 'Mostrar t\u00edtulo',
Left: 'Izquierda',
Center: 'Centrado',
Right: 'Derecha',
'Cell type': 'Tipo de celda',
Scope: '\u00c1mbito',
Alignment: 'Alineaci\u00f3n',
'H Align': 'Alineamiento Horizontal',
'V Align': 'Alineamiento Vertical',
Top: 'Arriba',
Middle: 'Centro',
Bottom: 'Abajo',
'Header cell': 'Celda de la cebecera',
'Row group': 'Grupo de filas',
'Column group': 'Grupo de columnas',
'Row type': 'Tipo de fila',
Header: 'Cabecera',
Body: 'Cuerpo',
Footer: 'Pie de p\u00e1gina',
'Border color': 'Color del borde',
'Insert template...': 'Insertar plantilla...',
Templates: 'Plantillas',
Template: 'Plantilla',
'Text color': 'Color del texto',
'Background color': 'Color de fondo',
'Custom...': 'Personalizar...',
'Custom color': 'Color personalizado',
'No color': 'Sin color',
'Remove color': 'Quitar color',
'Table of Contents': 'Tabla de contenidos',
'Show blocks': 'Mostrar bloques',
'Show invisible characters': 'Mostrar caracteres invisibles',
'Word count': 'Contar palabras',
Count: 'Recuento',
Document: 'Documento',
Selection: 'Selecci\u00f3n',
Words: 'Palabras',
'Words: {0}': 'Palabras: {0}',
'{0} words': '{0} palabras',
File: 'Archivo',
Edit: 'Editar',
Insert: 'Insertar',
View: 'Ver',
Format: 'Formato',
Table: 'Tabla',
Tools: 'Herramientas',
'Powered by {0}': 'Desarrollado por {0}',
'Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help': '\u00c1rea de texto enriquecido. Pulse ALT-F9 para el menu. Pulse ALT-F10 para la barra de herramientas. Pulse ALT-0 para ayuda',
'Image title': 'Titulo de imagen',
'Border width': 'Ancho de borde',
'Border style': 'Estilo de borde',
Error: 'Error',
Warn: 'Advertencia',
Valid: 'V\u00e1lido',
'To open the popup, press Shift+Enter': 'Para abrir el elemento emergente, pulse May\u00fas+Intro',
'Rich Text Area. Press ALT-0 for help.': '\u00c1rea de texto enriquecido. Pulse ALT-0 para abrir la ayuda.',
'System Font': 'Fuente de sistema',
'Failed to upload image: {0}': 'Fallo al cargar imagen: {0}',
'Failed to load plugin: {0} from url {1}': 'Fallo al cargar complemento: {0} desde URL {1}',
'Failed to load plugin url: {0}': 'Fallo al cargar URL del complemento: {0}',
'Failed to initialize plugin: {0}': 'Fallo al iniciar el complemento: {0}',
example: 'ejemplo',
Search: 'Buscar',
All: 'Todo',
Currency: 'Divisa',
Text: 'Texto',
Quotations: 'Comillas',
Mathematical: 'S\u00edmbolo matem\u00e1tico',
'Extended Latin': 'Latino extendido A',
Symbols: 'S\u00edmbolos',
Arrows: 'Flechas',
'User Defined': 'Definido por el usuario',
'dollar sign': 'signo de d\u00f3lar',
'currency sign': 'signo de divisa',
'euro-currency sign': 'signo de euro',
'colon sign': 'signo de dos puntos',
'cruzeiro sign': 'signo de cruceiro',
'french franc sign': 'signo de franco franc\u00e9s',
'lira sign': 'signo de lira',
'mill sign': 'signo de mill',
'naira sign': 'signo de naira',
'peseta sign': 'signo de peseta',
'rupee sign': 'signo de rupia',
'won sign': 'signo de won',
'new sheqel sign': 'signo de nuevo s\u00e9quel',
'dong sign': 'signo de dong',
'kip sign': 'signo de kip',
'tugrik sign': 'signo de tugrik',
'drachma sign': 'signo de dracma',
'german penny symbol': 'signo de penique alem\u00e1n',
'peso sign': 'signo de peso',
'guarani sign': 'signo de guaran\u00ed',
'austral sign': 'signo de austral',
'hryvnia sign': 'signo de grivna',
'cedi sign': 'signo de cedi',
'livre tournois sign': 'signo de libra tornesa',
'spesmilo sign': 'signo de spesmilo',
'tenge sign': 'signo de tenge',
'indian rupee sign': 'signo de rupia india',
'turkish lira sign': 'signo de lira turca',
'nordic mark sign': 'signo de marco n\u00f3rdico',
'manat sign': 'signo de manat',
'ruble sign': 'signo de rublo',
'yen character': 'car\u00e1cter de yen',
'yuan character': 'car\u00e1cter de yuan',
'yuan character, in hong kong and taiwan': 'car\u00e1cter de yuan en Hong Kong y Taiw\u00e1n',
'yen\/yuan character variant one': 'Variante uno de car\u00e1cter de yen\/yuan',
'Loading emoticons...': 'Cargando emoticonos...',
'Could not load emoticons': 'No se han podido cargar los emoticonos',
People: 'Personas',
'Animals and Nature': 'Animales y naturaleza',
'Food and Drink': 'Comida y bebida',
Activity: 'Actividad',
'Travel and Places': 'Viajes y lugares',
Objects: 'Objetos',
Flags: 'Banderas',
Characters: 'Caracteres',
'Characters (no spaces)': 'Caracteres (sin espacios)',
'{0} characters': '{0} caracteres',
'Error: Form submit field collision.': 'Error: Colisi\u00f3n de campo al enviar formulario.',
'Error: No form element found.': 'Error: No se encuentra ning\u00fan elemento de formulario.',
Update: 'Actualizar',
'Color swatch': 'Muestrario de colores',
Turquoise: 'Turquesa',
Green: 'Verde',
Blue: 'Azul',
Purple: 'P\u00farpura',
'Navy Blue': 'Azul marino',
'Dark Turquoise': 'Turquesa oscuro',
'Dark Green': 'Verde oscuro',
'Medium Blue': 'Azul medio',
'Medium Purple': 'P\u00farpura medio',
'Midnight Blue': 'Azul medio',
Yellow: 'Amarillo',
Orange: 'Naranja',
Red: 'Rojo',
'Light Gray': 'Gris claro',
Gray: 'Gris',
'Dark Yellow': 'Amarillo oscuro',
'Dark Orange': 'Naranja oscuro',
'Dark Red': 'Rojo oscuro',
'Medium Gray': 'Gris medio',
'Dark Gray': 'Gris oscuro',
'Light Green': 'Verde claro',
'Light Yellow': 'Amarillo claro',
'Light Red': 'Rojo claro',
'Light Purple': 'Morado claro',
'Light Blue': 'Azul claro',
'Dark Purple': 'Morado oscuro',
'Dark Blue': 'Azul oscuro',
Black: 'Negro',
White: 'Blanco',
'Switch to or from fullscreen mode': 'Activar o desactivar modo pantalla completa',
'Open help dialog': 'Abrir di\u00e1logo de ayuda',
history: 'historial',
styles: 'estilos',
formatting: 'formato',
alignment: 'alineaci\u00f3n',
indentation: 'sangr\u00eda',
'permanent pen': 'bol\u00edgrafo permanente',
comments: 'comentarios',
'Format Painter': 'Copiar formato',
'Insert\/edit iframe': 'Insertar\/editar iframe',
Capitalization: 'Uso de may\u00fasculas',
lowercase: 'min\u00fasculas',
UPPERCASE: 'MAY\u00daSCULAS',
'Title Case': 'Tipo T\u00edtulo',
'Permanent Pen Properties': 'Propiedades del bol\u00edgrafo permanente',
'Permanent pen properties...': 'Propiedades del bol\u00edgrafo permanente...',
Font: 'Fuente',
Size: 'Tama\u00f1o',
'More...': 'M\u00e1s...',
'Spellcheck Language': 'Corrector',
'Select...': 'Seleccionar...',
Preferences: 'Preferencias',
Yes: 'S\u00ed',
No: 'No',
'Keyboard Navigation': 'Navegaci\u00f3n con el teclado',
Version: 'Versi\u00f3n',
Anchor: 'Ancla',
'Special character': 'Car\u00e1cter especial',
'Code sample': 'Ejemplo de c\u00f3digo',
Color: 'Color',
Emoticons: 'Emoticonos',
'Document properties': 'Propiedades del documento',
Image: 'Imagen',
'Insert link': 'Insertar enlace',
Target: 'Destino',
Link: 'Enlace',
Poster: 'Miniatura',
Media: 'Media',
Print: 'Imprimir',
Prev: 'Anterior',
'Find and replace': 'Buscar y reemplazar',
'Whole words': 'Palabras completas',
Spellcheck: 'Corrector ortogr\u00e1fico',
Caption: 'Subt\u00edtulo',
'Insert template': 'Insertar plantilla'
})

@ -0,0 +1,389 @@
tinymce.addI18n('zh_CN',{
"Redo": "\u91cd\u505a",
"Undo": "\u64a4\u9500",
"Cut": "\u526a\u5207",
"Copy": "\u590d\u5236",
"Paste": "\u7c98\u8d34",
"Select all": "\u5168\u9009",
"New document": "\u65b0\u6587\u4ef6",
"Ok": "\u786e\u5b9a",
"Cancel": "\u53d6\u6d88",
"Visual aids": "\u7f51\u683c\u7ebf",
"Bold": "\u7c97\u4f53",
"Italic": "\u659c\u4f53",
"Underline": "\u4e0b\u5212\u7ebf",
"Strikethrough": "\u5220\u9664\u7ebf",
"Superscript": "\u4e0a\u6807",
"Subscript": "\u4e0b\u6807",
"Clear formatting": "\u6e05\u9664\u683c\u5f0f",
"Align left": "\u5de6\u8fb9\u5bf9\u9f50",
"Align center": "\u4e2d\u95f4\u5bf9\u9f50",
"Align right": "\u53f3\u8fb9\u5bf9\u9f50",
"Justify": "\u4e24\u7aef\u5bf9\u9f50",
"Bullet list": "\u9879\u76ee\u7b26\u53f7",
"Numbered list": "\u7f16\u53f7\u5217\u8868",
"Decrease indent": "\u51cf\u5c11\u7f29\u8fdb",
"Increase indent": "\u589e\u52a0\u7f29\u8fdb",
"Close": "\u5173\u95ed",
"Formats": "\u683c\u5f0f",
"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u6253\u5f00\u526a\u8d34\u677f\uff0c\u8bf7\u4f7f\u7528Ctrl+X\/C\/V\u7b49\u5feb\u6377\u952e\u3002",
"Headers": "\u6807\u9898",
"Header 1": "\u6807\u98981",
"Header 2": "\u6807\u98982",
"Header 3": "\u6807\u98983",
"Header 4": "\u6807\u98984",
"Header 5": "\u6807\u98985",
"Header 6": "\u6807\u98986",
"Headings": "\u6807\u9898",
"Heading 1": "\u6807\u98981",
"Heading 2": "\u6807\u98982",
"Heading 3": "\u6807\u98983",
"Heading 4": "\u6807\u98984",
"Heading 5": "\u6807\u98985",
"Heading 6": "\u6807\u98986",
"Preformatted": "\u9884\u5148\u683c\u5f0f\u5316\u7684",
"Div": "Div",
"Pre": "Pre",
"Code": "\u4ee3\u7801",
"Paragraph": "\u6bb5\u843d",
"Blockquote": "\u5f15\u6587\u533a\u5757",
"Inline": "\u6587\u672c",
"Blocks": "\u57fa\u5757",
"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002",
"Fonts": "\u5b57\u4f53",
"Font Sizes": "\u5b57\u53f7",
"Class": "\u7c7b\u578b",
"Browse for an image": "\u6d4f\u89c8\u56fe\u50cf",
"OR": "\u6216",
"Drop an image here": "\u62d6\u653e\u4e00\u5f20\u56fe\u50cf\u81f3\u6b64",
"Upload": "\u4e0a\u4f20",
"Block": "\u5757",
"Align": "\u5bf9\u9f50",
"Default": "\u9ed8\u8ba4",
"Circle": "\u7a7a\u5fc3\u5706",
"Disc": "\u5b9e\u5fc3\u5706",
"Square": "\u65b9\u5757",
"Lower Alpha": "\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd",
"Lower Greek": "\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd",
"Lower Roman": "\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd",
"Upper Alpha": "\u5927\u5199\u82f1\u6587\u5b57\u6bcd",
"Upper Roman": "\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd",
"Anchor...": "\u951a\u70b9...",
"Name": "\u540d\u79f0",
"Id": "\u6807\u8bc6\u7b26",
"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002",
"You have unsaved changes are you sure you want to navigate away?": "\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f",
"Restore last draft": "\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f",
"Special characters...": "\u7279\u6b8a\u5b57\u7b26...",
"Source code": "\u6e90\u4ee3\u7801",
"Insert\/Edit code sample": "\u63d2\u5165\/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b",
"Language": "\u8bed\u8a00",
"Code sample...": "\u793a\u4f8b\u4ee3\u7801...",
"Color Picker": "\u9009\u8272\u5668",
"R": "R",
"G": "G",
"B": "B",
"Left to right": "\u4ece\u5de6\u5230\u53f3",
"Right to left": "\u4ece\u53f3\u5230\u5de6",
"Emoticons...": "\u8868\u60c5\u7b26\u53f7...",
"Metadata and Document Properties": "\u5143\u6570\u636e\u548c\u6587\u6863\u5c5e\u6027",
"Title": "\u6807\u9898",
"Keywords": "\u5173\u952e\u8bcd",
"Description": "\u63cf\u8ff0",
"Robots": "\u673a\u5668\u4eba",
"Author": "\u4f5c\u8005",
"Encoding": "\u7f16\u7801",
"Fullscreen": "\u5168\u5c4f",
"Action": "\u64cd\u4f5c",
"Shortcut": "\u5feb\u6377\u952e",
"Help": "\u5e2e\u52a9",
"Address": "\u5730\u5740",
"Focus to menubar": "\u79fb\u52a8\u7126\u70b9\u5230\u83dc\u5355\u680f",
"Focus to toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u5de5\u5177\u680f",
"Focus to element path": "\u79fb\u52a8\u7126\u70b9\u5230\u5143\u7d20\u8def\u5f84",
"Focus to contextual toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u4e0a\u4e0b\u6587\u83dc\u5355",
"Insert link (if link plugin activated)": "\u63d2\u5165\u94fe\u63a5 (\u5982\u679c\u94fe\u63a5\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
"Save (if save plugin activated)": "\u4fdd\u5b58(\u5982\u679c\u4fdd\u5b58\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
"Find (if searchreplace plugin activated)": "\u67e5\u627e(\u5982\u679c\u67e5\u627e\u66ff\u6362\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
"Plugins installed ({0}):": "\u5df2\u5b89\u88c5\u63d2\u4ef6 ({0}):",
"Premium plugins:": "\u4f18\u79c0\u63d2\u4ef6\uff1a",
"Learn more...": "\u4e86\u89e3\u66f4\u591a...",
"You are using {0}": "\u4f60\u6b63\u5728\u4f7f\u7528 {0}",
"Plugins": "\u63d2\u4ef6",
"Handy Shortcuts": "\u5feb\u6377\u952e",
"Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf",
"Insert\/edit image": "\u63d2\u5165\/\u7f16\u8f91\u56fe\u7247",
"Image description": "\u56fe\u7247\u63cf\u8ff0",
"Source": "\u5730\u5740",
"Dimensions": "\u5927\u5c0f",
"Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4",
"General": "\u666e\u901a",
"Advanced": "\u9ad8\u7ea7",
"Style": "\u6837\u5f0f",
"Vertical space": "\u5782\u76f4\u8fb9\u8ddd",
"Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd",
"Border": "\u8fb9\u6846",
"Insert image": "\u63d2\u5165\u56fe\u7247",
"Image...": "\u56fe\u7247...",
"Image list": "\u56fe\u7247\u5217\u8868",
"Rotate counterclockwise": "\u9006\u65f6\u9488\u65cb\u8f6c",
"Rotate clockwise": "\u987a\u65f6\u9488\u65cb\u8f6c",
"Flip vertically": "\u5782\u76f4\u7ffb\u8f6c",
"Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c",
"Edit image": "\u7f16\u8f91\u56fe\u7247",
"Image options": "\u56fe\u7247\u9009\u9879",
"Zoom in": "\u653e\u5927",
"Zoom out": "\u7f29\u5c0f",
"Crop": "\u88c1\u526a",
"Resize": "\u8c03\u6574\u5927\u5c0f",
"Orientation": "\u65b9\u5411",
"Brightness": "\u4eae\u5ea6",
"Sharpen": "\u9510\u5316",
"Contrast": "\u5bf9\u6bd4\u5ea6",
"Color levels": "\u989c\u8272\u5c42\u6b21",
"Gamma": "\u4f3d\u9a6c\u503c",
"Invert": "\u53cd\u8f6c",
"Apply": "\u5e94\u7528",
"Back": "\u540e\u9000",
"Insert date\/time": "\u63d2\u5165\u65e5\u671f\/\u65f6\u95f4",
"Date\/time": "\u65e5\u671f\/\u65f6\u95f4",
"Insert\/Edit Link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
"Insert\/edit link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
"Text to display": "\u663e\u793a\u6587\u5b57",
"Url": "\u5730\u5740",
"Open link in...": "\u94fe\u63a5\u6253\u5f00\u4f4d\u7f6e...",
"Current window": "\u5f53\u524d\u7a97\u53e3",
"None": "\u65e0",
"New window": "\u5728\u65b0\u7a97\u53e3\u6253\u5f00",
"Remove link": "\u5220\u9664\u94fe\u63a5",
"Anchors": "\u951a\u70b9",
"Link...": "\u94fe\u63a5...",
"Paste or type a link": "\u7c98\u8d34\u6216\u8f93\u5165\u94fe\u63a5",
"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u4e3a\u90ae\u4ef6\u5730\u5740\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7f00\u5417\uff1f",
"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u5c5e\u4e8e\u5916\u90e8\u94fe\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:\/\/:\u524d\u7f00\u5417\uff1f",
"Link list": "\u94fe\u63a5\u5217\u8868",
"Insert video": "\u63d2\u5165\u89c6\u9891",
"Insert\/edit video": "\u63d2\u5165\/\u7f16\u8f91\u89c6\u9891",
"Insert\/edit media": "\u63d2\u5165\/\u7f16\u8f91\u5a92\u4f53",
"Alternative source": "\u955c\u50cf",
"Alternative source URL": "\u66ff\u4ee3\u6765\u6e90\u7f51\u5740",
"Media poster (Image URL)": "\u5c01\u9762(\u56fe\u7247\u5730\u5740)",
"Paste your embed code below:": "\u5c06\u5185\u5d4c\u4ee3\u7801\u7c98\u8d34\u5728\u4e0b\u9762:",
"Embed": "\u5185\u5d4c",
"Media...": "\u591a\u5a92\u4f53...",
"Nonbreaking space": "\u4e0d\u95f4\u65ad\u7a7a\u683c",
"Page break": "\u5206\u9875\u7b26",
"Paste as text": "\u7c98\u8d34\u4e3a\u6587\u672c",
"Preview": "\u9884\u89c8",
"Print...": "\u6253\u5370...",
"Save": "\u4fdd\u5b58",
"Find": "\u67e5\u627e",
"Replace with": "\u66ff\u6362\u4e3a",
"Replace": "\u66ff\u6362",
"Replace all": "\u5168\u90e8\u66ff\u6362",
"Previous": "\u4e0a\u4e00\u4e2a",
"Next": "\u4e0b\u4e00\u4e2a",
"Find and replace...": "\u67e5\u627e\u5e76\u66ff\u6362...",
"Could not find the specified string.": "\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.",
"Match case": "\u533a\u5206\u5927\u5c0f\u5199",
"Find whole words only": "\u5168\u5b57\u5339\u914d",
"Spell check": "\u62fc\u5199\u68c0\u67e5",
"Ignore": "\u5ffd\u7565",
"Ignore all": "\u5168\u90e8\u5ffd\u7565",
"Finish": "\u5b8c\u6210",
"Add to Dictionary": "\u6dfb\u52a0\u5230\u5b57\u5178",
"Insert table": "\u63d2\u5165\u8868\u683c",
"Table properties": "\u8868\u683c\u5c5e\u6027",
"Delete table": "\u5220\u9664\u8868\u683c",
"Cell": "\u5355\u5143\u683c",
"Row": "\u884c",
"Column": "\u5217",
"Cell properties": "\u5355\u5143\u683c\u5c5e\u6027",
"Merge cells": "\u5408\u5e76\u5355\u5143\u683c",
"Split cell": "\u62c6\u5206\u5355\u5143\u683c",
"Insert row before": "\u5728\u4e0a\u65b9\u63d2\u5165",
"Insert row after": "\u5728\u4e0b\u65b9\u63d2\u5165",
"Delete row": "\u5220\u9664\u884c",
"Row properties": "\u884c\u5c5e\u6027",
"Cut row": "\u526a\u5207\u884c",
"Copy row": "\u590d\u5236\u884c",
"Paste row before": "\u7c98\u8d34\u5230\u4e0a\u65b9",
"Paste row after": "\u7c98\u8d34\u5230\u4e0b\u65b9",
"Insert column before": "\u5728\u5de6\u4fa7\u63d2\u5165",
"Insert column after": "\u5728\u53f3\u4fa7\u63d2\u5165",
"Delete column": "\u5220\u9664\u5217",
"Cols": "\u5217",
"Rows": "\u884c",
"Width": "\u5bbd",
"Height": "\u9ad8",
"Cell spacing": "\u5355\u5143\u683c\u5916\u95f4\u8ddd",
"Cell padding": "\u5355\u5143\u683c\u5185\u8fb9\u8ddd",
"Show caption": "\u663e\u793a\u6807\u9898",
"Left": "\u5de6\u5bf9\u9f50",
"Center": "\u5c45\u4e2d",
"Right": "\u53f3\u5bf9\u9f50",
"Cell type": "\u5355\u5143\u683c\u7c7b\u578b",
"Scope": "\u8303\u56f4",
"Alignment": "\u5bf9\u9f50\u65b9\u5f0f",
"H Align": "\u6c34\u5e73\u5bf9\u9f50",
"V Align": "\u5782\u76f4\u5bf9\u9f50",
"Top": "\u9876\u90e8\u5bf9\u9f50",
"Middle": "\u5782\u76f4\u5c45\u4e2d",
"Bottom": "\u5e95\u90e8\u5bf9\u9f50",
"Header cell": "\u8868\u5934\u5355\u5143\u683c",
"Row group": "\u884c\u7ec4",
"Column group": "\u5217\u7ec4",
"Row type": "\u884c\u7c7b\u578b",
"Header": "\u8868\u5934",
"Body": "\u8868\u4f53",
"Footer": "\u8868\u5c3e",
"Border color": "\u8fb9\u6846\u989c\u8272",
"Insert template...": "\u63d2\u5165\u6a21\u677f...",
"Templates": "\u6a21\u677f",
"Template": "\u6a21\u677f",
"Text color": "\u6587\u5b57\u989c\u8272",
"Background color": "\u80cc\u666f\u8272",
"Custom...": "\u81ea\u5b9a\u4e49...",
"Custom color": "\u81ea\u5b9a\u4e49\u989c\u8272",
"No color": "\u65e0",
"Remove color": "\u79fb\u9664\u989c\u8272",
"Table of Contents": "\u5185\u5bb9\u5217\u8868",
"Show blocks": "\u663e\u793a\u533a\u5757\u8fb9\u6846",
"Show invisible characters": "\u663e\u793a\u4e0d\u53ef\u89c1\u5b57\u7b26",
"Word count": "\u5b57\u6570",
"Words: {0}": "\u5b57\u6570\uff1a{0}",
"{0} words": "{0} \u5b57",
"File": "\u6587\u4ef6",
"Edit": "\u7f16\u8f91",
"Insert": "\u63d2\u5165",
"View": "\u89c6\u56fe",
"Format": "\u683c\u5f0f",
"Table": "\u8868\u683c",
"Tools": "\u5de5\u5177",
"Powered by {0}": "\u7531{0}\u9a71\u52a8",
"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u5728\u7f16\u8f91\u533a\u6309ALT-F9\u6253\u5f00\u83dc\u5355\uff0c\u6309ALT-F10\u6253\u5f00\u5de5\u5177\u680f\uff0c\u6309ALT-0\u67e5\u770b\u5e2e\u52a9",
"Image title": "\u56fe\u7247\u6807\u9898",
"Border width": "\u8fb9\u6846\u5bbd\u5ea6",
"Border style": "\u8fb9\u6846\u6837\u5f0f",
"Error": "\u9519\u8bef",
"Warn": "\u8b66\u544a",
"Valid": "\u6709\u6548",
"To open the popup, press Shift+Enter": "\u6309Shitf+Enter\u952e\u6253\u5f00\u5bf9\u8bdd\u6846",
"Rich Text Area. Press ALT-0 for help.": "\u7f16\u8f91\u533a\u3002\u6309Alt+0\u952e\u6253\u5f00\u5e2e\u52a9\u3002",
"System Font": "\u7cfb\u7edf\u5b57\u4f53",
"Failed to upload image: {0}": "\u56fe\u7247\u4e0a\u4f20\u5931\u8d25: {0}",
"Failed to load plugin: {0} from url {1}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25: {0} \u6765\u81ea\u94fe\u63a5 {1}",
"Failed to load plugin url: {0}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25 \u94fe\u63a5: {0}",
"Failed to initialize plugin: {0}": "\u63d2\u4ef6\u521d\u59cb\u5316\u5931\u8d25: {0}",
"example": "\u793a\u4f8b",
"Search": "\u641c\u7d22",
"All": "\u5168\u90e8",
"Currency": "\u8d27\u5e01",
"Text": "\u6587\u5b57",
"Quotations": "\u5f15\u7528",
"Mathematical": "\u6570\u5b66",
"Extended Latin": "\u62c9\u4e01\u8bed\u6269\u5145",
"Symbols": "\u7b26\u53f7",
"Arrows": "\u7bad\u5934",
"User Defined": "\u81ea\u5b9a\u4e49",
"dollar sign": "\u7f8e\u5143\u7b26\u53f7",
"currency sign": "\u8d27\u5e01\u7b26\u53f7",
"euro-currency sign": "\u6b27\u5143\u7b26\u53f7",
"colon sign": "\u5192\u53f7",
"cruzeiro sign": "\u514b\u9c81\u8d5b\u7f57\u5e01\u7b26\u53f7",
"french franc sign": "\u6cd5\u90ce\u7b26\u53f7",
"lira sign": "\u91cc\u62c9\u7b26\u53f7",
"mill sign": "\u5bc6\u5c14\u7b26\u53f7",
"naira sign": "\u5948\u62c9\u7b26\u53f7",
"peseta sign": "\u6bd4\u585e\u5854\u7b26\u53f7",
"rupee sign": "\u5362\u6bd4\u7b26\u53f7",
"won sign": "\u97e9\u5143\u7b26\u53f7",
"new sheqel sign": "\u65b0\u8c22\u514b\u5c14\u7b26\u53f7",
"dong sign": "\u8d8a\u5357\u76fe\u7b26\u53f7",
"kip sign": "\u8001\u631d\u57fa\u666e\u7b26\u53f7",
"tugrik sign": "\u56fe\u683c\u91cc\u514b\u7b26\u53f7",
"drachma sign": "\u5fb7\u62c9\u514b\u9a6c\u7b26\u53f7",
"german penny symbol": "\u5fb7\u56fd\u4fbf\u58eb\u7b26\u53f7",
"peso sign": "\u6bd4\u7d22\u7b26\u53f7",
"guarani sign": "\u74dc\u62c9\u5c3c\u7b26\u53f7",
"austral sign": "\u6fb3\u5143\u7b26\u53f7",
"hryvnia sign": "\u683c\u91cc\u592b\u5c3c\u4e9a\u7b26\u53f7",
"cedi sign": "\u585e\u5730\u7b26\u53f7",
"livre tournois sign": "\u91cc\u5f17\u5f17\u5c14\u7b26\u53f7",
"spesmilo sign": "spesmilo\u7b26\u53f7",
"tenge sign": "\u575a\u6208\u7b26\u53f7",
"indian rupee sign": "\u5370\u5ea6\u5362\u6bd4",
"turkish lira sign": "\u571f\u8033\u5176\u91cc\u62c9",
"nordic mark sign": "\u5317\u6b27\u9a6c\u514b",
"manat sign": "\u9a6c\u7eb3\u7279\u7b26\u53f7",
"ruble sign": "\u5362\u5e03\u7b26\u53f7",
"yen character": "\u65e5\u5143\u5b57\u6837",
"yuan character": "\u4eba\u6c11\u5e01\u5143\u5b57\u6837",
"yuan character, in hong kong and taiwan": "\u5143\u5b57\u6837\uff08\u6e2f\u53f0\u5730\u533a\uff09",
"yen\/yuan character variant one": "\u5143\u5b57\u6837\uff08\u5927\u5199\uff09",
"Loading emoticons...": "\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7...",
"Could not load emoticons": "\u4e0d\u80fd\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7",
"People": "\u4eba\u7c7b",
"Animals and Nature": "\u52a8\u7269\u548c\u81ea\u7136",
"Food and Drink": "\u98df\u7269\u548c\u996e\u54c1",
"Activity": "\u6d3b\u52a8",
"Travel and Places": "\u65c5\u6e38\u548c\u5730\u70b9",
"Objects": "\u7269\u4ef6",
"Flags": "\u65d7\u5e1c",
"Characters": "\u5b57\u7b26",
"Characters (no spaces)": "\u5b57\u7b26(\u65e0\u7a7a\u683c)",
"Error: Form submit field collision.": "\u9519\u8bef: \u8868\u5355\u63d0\u4ea4\u5b57\u6bb5\u51b2\u7a81\u3002",
"Error: No form element found.": "\u9519\u8bef: \u6ca1\u6709\u8868\u5355\u63a7\u4ef6\u3002",
"Update": "\u66f4\u65b0",
"Color swatch": "\u989c\u8272\u6837\u672c",
"Turquoise": "\u9752\u7eff\u8272",
"Green": "\u7eff\u8272",
"Blue": "\u84dd\u8272",
"Purple": "\u7d2b\u8272",
"Navy Blue": "\u6d77\u519b\u84dd",
"Dark Turquoise": "\u6df1\u84dd\u7eff\u8272",
"Dark Green": "\u6df1\u7eff\u8272",
"Medium Blue": "\u4e2d\u84dd\u8272",
"Medium Purple": "\u4e2d\u7d2b\u8272",
"Midnight Blue": "\u6df1\u84dd\u8272",
"Yellow": "\u9ec4\u8272",
"Orange": "\u6a59\u8272",
"Red": "\u7ea2\u8272",
"Light Gray": "\u6d45\u7070\u8272",
"Gray": "\u7070\u8272",
"Dark Yellow": "\u6697\u9ec4\u8272",
"Dark Orange": "\u6df1\u6a59\u8272",
"Dark Red": "\u6df1\u7ea2\u8272",
"Medium Gray": "\u4e2d\u7070\u8272",
"Dark Gray": "\u6df1\u7070\u8272",
"Black": "\u9ed1\u8272",
"White": "\u767d\u8272",
"Switch to or from fullscreen mode": "\u5207\u6362\u5168\u5c4f\u6a21\u5f0f",
"Open help dialog": "\u6253\u5f00\u5e2e\u52a9\u5bf9\u8bdd\u6846",
"history": "\u5386\u53f2",
"styles": "\u6837\u5f0f",
"formatting": "\u683c\u5f0f\u5316",
"alignment": "\u5bf9\u9f50",
"indentation": "\u7f29\u8fdb",
"permanent pen": "\u8bb0\u53f7\u7b14",
"comments": "\u5907\u6ce8",
"Anchor": "\u951a\u70b9",
"Special character": "\u7279\u6b8a\u7b26\u53f7",
"Code sample": "\u4ee3\u7801\u793a\u4f8b",
"Color": "\u989c\u8272",
"Emoticons": "\u8868\u60c5",
"Document properties": "\u6587\u6863\u5c5e\u6027",
"Image": "\u56fe\u7247",
"Insert link": "\u63d2\u5165\u94fe\u63a5",
"Target": "\u6253\u5f00\u65b9\u5f0f",
"Link": "\u94fe\u63a5",
"Poster": "\u5c01\u9762",
"Media": "\u5a92\u4f53",
"Print": "\u6253\u5370",
"Prev": "\u4e0a\u4e00\u4e2a",
"Find and replace": "\u67e5\u627e\u548c\u66ff\u6362",
"Whole words": "\u5168\u5b57\u5339\u914d",
"Spellcheck": "\u62fc\u5199\u68c0\u67e5",
"Caption": "\u6807\u9898",
"Insert template": "\u63d2\u5165\u6a21\u677f"
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,7 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{background-color:green;display:inline-block;opacity:.5;position:absolute}body{-webkit-text-size-adjust:none}body img{max-width:96vw}body table img{max-width:95%}body{font-family:sans-serif}table{border-collapse:collapse}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,7 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;-ms-scroll-chaining:none;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,7 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{background-color:green;display:inline-block;opacity:.5;position:absolute}body{-webkit-text-size-adjust:none}body img{max-width:96vw}body table img{max-width:95%}body{font-family:sans-serif}table{border-collapse:collapse}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,7 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;-ms-scroll-chaining:none;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}

@ -0,0 +1,78 @@
<template>
<div :class="getAppLogoClass" @click="goHome">
<div class="ml-2 truncate md:opacity-100" :class="getTitleClass">
{{ title }}
</div>
</div>
</template>
<script lang="ts" setup>
import { useDesign } from '~/composables/useDesign';
const { prefixCls } = useDesign('app-logo');
const title = import.meta.env.VITE_GLOB_APP_TITLE
const getAppLogoClass = computed(() => [
prefixCls
]);
const getTitleClass = computed(() => [
`${prefixCls}__title`,
]);
const router = useRouter()
function goHome() {
router.push('/')
}
</script>
<style lang="scss" scoped>
$prefix-cls: '#{$name-prefix}-app-logo';
$header-prefix-cls: '#{$name-prefix}-layout-header';
.#{$header-prefix-cls} {
&--white {
.#{$prefix-cls} {
&__title {
//color: @white;
color: #333 !important;
}
}
}
&--host {
.#{$prefix-cls} {
&__title {
color: $white !important;
//color: #333;
}
}
}
}
.#{$prefix-cls} {
display: flex;
align-items: center;
padding-left: 7px;
cursor: pointer;
transition: all 0.2s ease;
&.light {
//border-bottom: 1px solid @border-color-base;
}
&.collapsed-show-title {
padding-left: 20px;
}
&.light &__title {
//color: @primary-color;
color: #333;
}
&.dark &__title {
//color: @white;
color: #333;
}
&__title {
font-size: 28px;
font-weight: 700;
transition: all 0.5s;
line-height: normal;
}
}
</style>

@ -0,0 +1,58 @@
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { useDesign } from '~/composables/useDesign';
// import SvgIcon from '/@/components/Icon/src/SvgIcon.vue';
import banner from '~~/assets/images/homeBanner.png';
export default defineComponent({
// components: { SvgIcon },
emits: ['search'],
setup(_, ctx) {
const { prefixCls } = useDesign('banner');
const imgUrl = ref(banner);
const searchVal = ref('');
function onInput(ev: { target: { value: string; }; }) {
searchVal.value = ev.target.value;
ctx.emit('search', ev);
}
// const imgUrl = ()=
return {
prefixCls,
imgUrl,
onInput,
searchVal,
};
},
});
</script>
<template>
<div :class="[prefixCls, `${prefixCls}--host`]">
<img :src="imgUrl" alt="" />
<div class="search-bar">
<el-icon :size="27" >
<svg width="27px" height="27px" viewBox="0 0 27 27" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>放大镜</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="首页" transform="translate(-416.000000, -351.000000)" fill="#000000" fill-rule="nonzero">
<g id="放大镜" transform="translate(416.000000, 351.000000)">
<path d="M25.853125,24.25 L20.128125,18.525 C21.73125,16.56875 22.69375,14.071875 22.69375,11.35 C22.69375,5.096875 17.60625,0.00625 11.35,0.00625 C5.096875,0.00625 0.00625,5.09375 0.00625,11.35 C0.00625,17.60625 5.09375,22.69375 11.35,22.69375 C14.06875,22.69375 16.56875,21.73125 18.525,20.128125 L24.25,25.853125 C24.471875,26.075 24.7625,26.184375 25.053125,26.184375 C25.34375,26.184375 25.634375,26.075 25.85625,25.853125 C26.296875,25.409375 26.296875,24.690625 25.853125,24.25 L25.853125,24.25 Z M2.275,11.35 C2.275,6.346875 6.346875,2.275 11.35,2.275 C16.353125,2.275 20.425,6.346875 20.425,11.35 C20.425,16.353125 16.353125,20.425 11.35,20.425 C6.346875,20.425 2.275,16.353125 2.275,11.35 Z" id="形状"></path>
</g>
</g>
</g>
</svg>
</el-icon>
<!-- <el-icon><Search /></el-icon>-->
<!-- <el-icon name="mai-search" />-->
<input
placeholder="按照关键词搜索"
:value="searchVal"
@input="onInput"
class="mai-input"
type="text"
/>
<button class="mai-btn" type="button">搜索</button>
</div>
</div>
</template>
<style lang="scss" scoped>
@import 'banner';
</style>

@ -0,0 +1,13 @@
<script lang="ts" setup>
const props = defineProps(['title']);
const { prefixCls } = useDesign('banner');
</script>
<template>
<div :class="[prefixCls, `${prefixCls}--white`]">
<img src="~/assets/images/commonBanner.png" alt="" />
<h2 class="title">{{ props.title }}</h2>
</div>
</template>
<style lang="scss" scoped>
@import 'banner';
</style>

@ -0,0 +1,62 @@
$banner-prefix-cls: '#{$name-prefix}-banner';
$svgIcon-prefix-cls: '#{$name-prefix}-svg-icon';
.#{$banner-prefix-cls} {
width: 100%;
position: relative;
transition: margin-top .3s;
&--host {
margin-top: -100px;
}
.title {
font-size: 42px;
font-weight: bold;
color: $white;
position: absolute;
left: 160px;
top: 50%;
transform: translateY(-50%);
letter-spacing: 5px;
}
img {
width: 100%;
}
:deep(.search-bar) {
position: absolute;
bottom: 234px;
left: 50%;
border-radius: 4px;
transform: translateX(-50%);
width: 1152px;
height: 90px;
background-color: rgb(255 255 255 / 80%);
display: flex;
align-items: center;
.el-icon {
margin: 0 24px 0 32px;
}
.mai-input {
background: transparent;
outline: none;
border: none;
border-radius: unset;
width: 100%;
font-size: 28px;
padding-right: 24px;
}
.mai-btn {
font-size: 32px;
color: $white;
height: 100%;
background: #14417a;
border-radius: 4px;
width: 180px;
flex-shrink: 0;
}
}
}

@ -0,0 +1,48 @@
<template>
<h4 class="mai-section-title">{{ title }}</h4>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
props: {
title: {
type: String,
default: '',
},
},
setup() {},
});
</script>
<style scoped lang="less">
.mai-section-title {
font-size: 32px;
color: #333;
width: 1058px;
margin: 0 auto;
text-align: center;
position: relative;
margin-bottom: 82px;
&::after,
&::before {
position: absolute;
content: '';
top: 50%;
transform: translateY(-50%);
width: 40%;
height: 2px;
background: #ddd;
}
&::after {
right: 0;
}
&::before {
left: 0;
}
}
</style>

@ -0,0 +1,72 @@
<template>
<el-footer :class="prefixCls">
<div :class="`${prefixCls}__links`">
<nuxt-link :to="item.link" :key="index" v-for="(item, index) in menuList">{{
item.title
}}
</nuxt-link>
</div>
<div :class="`${prefixCls}__copyright`"
>Copyright © 2012-2025 北京中海通科技有限公司版权所有每ICP备xxxxxxxxxxxxxxx号
</div
>
</el-footer>
</template>
<script lang="ts" setup>
const menuList = ref([]);
const {prefixCls} = useDesign('layout-footer');
const {commonApi} = useApi();
async function getData() {
const menus = await commonApi.getBottomMenu()
menuList.value = menus.data
}
getData()
</script>
<style lang="scss" scoped>
$prefix-cls: '#{$name-prefix}-layout-footer';
$normal-color: #fff;
$hover-color: rgba(0, 0, 0, 0.85);
.#{$prefix-cls} {
height: fit-content;
padding: 40px 0 85px;
background-color: #022950;
color: $normal-color;
text-align: center;
&__links {
margin-bottom: 50px;
a {
font-size: 20px;
padding: 0 20px;
color: $normal-color;
border-right: 1px solid $white;
&:hover {
//color: @hover-color;
}
&:last-of-type {
border: unset;
}
}
}
&__github {
margin: 0 30px;
&:hover {
color: $hover-color;
}
}
&__copyright {
font-size: 16px;
}
}
</style>

@ -0,0 +1,108 @@
<template>
<el-header height="100px" :class="[prefixCls, 'flex', 'justify-between', store.getMenuTheme === 'dark'? 'is-host' : 'is-white']">
<ApplicationAppLogo />
<el-menu
:default-active="$route.meta.parentPath || $route.path"
class="el-menu-demo"
mode="horizontal"
:ellipsis="false"
router
>
<el-menu-item @click="handleChange(item)" v-for="item in routes" :index="item.path" :key="item.path">{{ item.name }}</el-menu-item>
</el-menu>
</el-header>
</template>
<script setup>
import {useDesign} from "/composables/useDesign";
// import {useAppStore} from "/stores/app";
const { prefixCls } = useDesign('header-wrap')
const route = useRoute()
const router = useRouter();
const routes = ref(router.getRoutes().filter(it=> !it.meta.hidden));
routes.value.sort((a,b)=> a.meta.order - b.meta.order);
const activeRoute = ref(route.path)
const store = useAppStore()
function handleChange(val) {
activeRoute.value = val.path;
if(val.meta.hasOwnProperty('headerHost') && val.meta.headerHost) {
store.setMenuTheme('dark')
}else {
store.setMenuTheme('light')
}
}
watch(()=> route, (val)=>{
activeRoute.value = val.path;
if(val.meta.hasOwnProperty('headerHost') && val.meta.headerHost) {
store.setMenuTheme('dark')
}else {
store.setMenuTheme('light')
}
}, {deep: true})
watchEffect(()=>{
handleChange(route)
})
</script>
<style scoped lang="scss">
$prefix-cls: '#{$name-prefix}-header-wrap';
.#{$prefix-cls} {
z-index: 1;
@apply relative;
:deep(.el-menu) {
@apply bg-transparent;
border-bottom: unset;
.el-menu-item {
border-bottom: unset;
//font-size: 20px;
transition: font-size .5s;
background-color: unset;
@apply relative text-xl;
&:after{
content: '';
width: 50%;
height: 8px;
border-radius: 5px;
left: 50%;
bottom: 0;
transform: translateX(-50%);
@apply bg-transparent absolute;
}
&:hover{
color: unset;
}
&.is-active {
color: #333 !important;
@apply font-bold text-2xl;
&:after{
@apply bg-black;
}
}
}
}
&.is-host{
:deep(.el-menu) {
.el-menu-item {
color: $white !important;
&:hover{
color: unset;
}
&.is-active {
&:after{
@apply bg-white;
}
}
}
}
:deep(.yangliu-app-logo__title){
color: $white;
}
}
//height: 100px;
}
</style>

@ -0,0 +1,47 @@
<template>
<iframe
id="preview-wrap"
class="preview-wrap"
sandbox="allow-scripts allow-same-origin"
data-alloy-tabstop="true"
tabindex="-1"
:srcdoc="`<!DOCTYPE html>
<html lang='zh-CN'>
<head>
<title>Preview</title>
<style>${CssP1}</style>
<style>${CssP2}</style>
</head>
<body style='height:fit-content' id='tinymce' class='mce-content-body'>
${props.content}<script>
document.addEventListener && document.addEventListener('click', function(e) {for (var elm = e.target; elm; elm = elm.parentNode) {if (elm.nodeName === 'A' && !(e.metaKey)) {e.preventDefault();}}}, false)
document.onreadystatechange = (state)=> {
if(document.readyState === 'complete') {
const oWrap = window.parent.document.querySelector('#preview-wrap');
oWrap.style.height = document.body.clientHeight + 50 + 'px'
}
}
</script>
</body>
</html>`"
>
</iframe>
</template>
<script setup lang="ts">
//@ts-ignore
import CssP1 from '/assets/tinymce/skins/ui/oxide/content.min.css?inline';
//@ts-ignore
import CssP2 from '/assets/tinymce/skins/ui/oxide/content.min.css?inline';
import { defineProps } from 'vue';
const props = defineProps(['content']);
</script>
<style scoped lang="scss">
.preview-wrap {
width: 100%;
height: fit-content;
//min-height: 600px;
}
</style>

@ -0,0 +1,92 @@
<script lang="tsx">
import { defineComponent } from 'vue';
interface DataSourceItem {
name: string;
id: string;
children: DataSourceItem[];
}
export default defineComponent({
props: {
dataSource: {
type: Array,
default() {
return [];
},
},
active: {
type: String,
required: true,
},
},
emits: ['change'],
//ts-ignore
setup(props, ctx) {
const { prefixCls } = useDesign('mai-tab');
function onChange(data: DataSourceItem, active: string) {
if (props.active === active) return;
ctx.emit('change', data, active);
}
return () => (
<>
{// @ts-ignore
props.dataSource.map((tab: DataSourceItem) => (
<div class={`${prefixCls}`} key={tab.id}>
<div class={`${prefixCls}--title-wrap flex justify-end`}>
<div class={`${prefixCls}--title-bg`}></div>
<h3 class={`${prefixCls}--title`}>{tab.name}</h3>
</div>
<div class={`${prefixCls}--item flex flex-col items-end`}>
{tab.children &&
tab.children.map((item: DataSourceItem) => (
<div
onClick={() => {
onChange(item, item.id);
}}
key={item.id}
class={[
`${prefixCls}--item-title`,
props.active === item.id ? `${prefixCls}--item-active` : '',
'flex justify-between items-center',
]}
>
<span class="title">{item.name}</span>
<el-icon><el-ArrowRightBold /></el-icon>
</div>
))}
</div>
</div>
))}
{/* <div class={`${prefixCls}`}>
<div class={`${prefixCls}--title-wrap flex justify-end`}>
<div class={`${prefixCls}--title-bg`}></div>
<h3 class={`${prefixCls}--title`}>{props.title}</h3>
</div>
<div class={`${prefixCls}--item flex flex-col items-end`}>
{props.dataSource.map((item: DataSourceItem, index) => (
<div
onClick={() => {
onChange(item, index);
}}
key={item.id}
class={[
`${prefixCls}--item-title`,
currentActive.value === index ? `${prefixCls}--item-active` : '',
'flex justify-between items-center',
]}
>
<span class="title">{item.title}</span>
<Icon icon="ant-design:right-outlined"></Icon>
</div>
))}
</div>
</div>*/}
</>
);
},
});
</script>
<style scoped lang="scss">
@import 'MaiTab';
</style>

@ -0,0 +1,96 @@
$prefix-cls: '#{$name-prefix}-mai-tab';
$mai-tab-bg: #022950;
.#{$prefix-cls} {
margin-bottom: 60px;
&--title-wrap {
position: relative;
width: 300px;
height: 50px;
margin-bottom: 45px;
.#{$prefix-cls}--title-bg {
position: absolute;
z-index: 1;
width: 230px;
height: 100%;
background-color: $mai-tab-bg;
transform: skewX(-30deg);
&::after,
&::before {
content: '';
position: absolute;
height: 100%;
}
&::after {
width: 95%;
background-color: transparent;
border-color: transparent;
border-width: 3px;
border-left-color: $mai-tab-bg;
border-bottom-color: $mai-tab-bg;
left: -10px;
bottom: -10px;
}
&::before {
width: 14%;
background-color: $mai-tab-bg;
left: -50px;
bottom: 0;
}
}
h3 {
//text-align: center;
//padding-right: 80px;
position: absolute;
left: 100px;
z-index: 2;
font-size: 24px;
color: $white;
line-height: 50px;
letter-spacing: 2px;
}
}
&--item {
width: 300px;
padding-right: 25px;
.title {
color: #999;
font-size: 20px;
width: 160px;
overflow: hidden;
text-overflow: ellipsis;
//margin-right: 90px;
}
&-title {
width: 180px;
cursor: pointer;
margin-bottom: 32px;
}
&-active {
position: relative;
&::before {
content: '';
position: absolute;
width: 8px;
height: 24px;
left: -30px;
background-color: $mai-tab-bg;
}
.title {
font-weight: 600;
color: $mai-tab-bg;
}
}
}
}

@ -0,0 +1,25 @@
<template>
<button class="mai-more-btn" @click="onClick"></button>
</template>
<script setup lang="ts">
import { defineProps } from 'vue';
const props = defineProps(['url']);
const router = useRouter();
function onClick() {
router.push(props.url);
}
</script>
<style lang="scss" scoped>
.mai-more-btn {
width: 270px;
height: 80px;
background: $white;
border-radius: 50px;
border: 1px solid #979797;
font-size: 28px;
}
</style>

@ -0,0 +1,54 @@
<template>
<div class="flex justify-center">
<el-button :disabled="current === 1 || total === 0" @click="changePage('prev')"></el-button>
<el-pagination
:current-page="current"
@current-change="onChangePage"
background
:page-size="limit"
layout="pager"
:total="total">
</el-pagination>
<el-button :disabled="current === pages || total === 0" @click="changePage('next')"></el-button>
</div>
</template>
<script setup lang="ts">
const props = defineProps(['total', 'current', 'pages', 'limit'])
const emits = defineEmits(['change'])
function onChangePage(val:number) {
console.log('val>>>>>>>>>>>>>>', val)
emits('change', val)
}
function changePage(flag: 'next'|'prev'):void {
if(flag === 'next') {
if (props.current >= props.pages) return;
else {
onChangePage(props.current + 1)
}
}else if(flag === 'prev') {
if (props.current <= 1) return;
else {
onChangePage(props.current - 1)
}
}
}
// emit
</script>
<style scoped lang="scss">
:deep(.el-pagination) {
.el-pager {
.number {
margin: 0 5px;
}
.number.is-active {
background-color: #022950;
@apply text-base;
}
}
}
</style>

@ -0,0 +1,3 @@
import Api from '~~/api'
export default () => Api;

@ -0,0 +1,32 @@
import {acceptHMRUpdate, createPinia, defineStore} from 'pinia';
type menu = 'light' | 'dark'
export interface AppState {
menuTheme: menu;
bannerTitle: string;
}
const useAppStore = defineStore('app', {
state: (): AppState => ({
menuTheme: 'light',
bannerTitle: ''
}),
getters:{
getMenuTheme: (state)=> state.menuTheme,
getBannerTitle: (state)=> state.bannerTitle
},
actions: {
setMenuTheme(type: menu = 'light'){
this.menuTheme = type;
},
setBannerTitle(title: string): void{
this.bannerTitle = title;
}
}
})
export default useAppStore;
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useAppStore, import.meta.hot))
}

@ -0,0 +1,10 @@
// import { useAppProviderContext } from '~/components/Application';
// import { computed } from 'vue';
// import { lowerFirst } from 'lodash-es';
export function useDesign(scope: string) {
return {
prefixCls: `${'yangliu'}-${scope}`,
prefixVar: 'yangliu',
// style,
};
}

@ -0,0 +1,40 @@
import {useDebounceFn, useThrottleFn} from "@vueuse/core";
export default function usePagination(current: number, limit: number, total: number) {
const currentRef = ref(current);
const pageSizeRef = ref(limit);
const totalRef = ref(total);
const pagesRef = ref(Math.ceil(totalRef.value / limit))
watchEffect(()=>{
pagesRef.value = Math.ceil(totalRef.value / limit)
})
function setCurrentPage(page: number) {
currentRef.value = page;
// if (isFunction(callback)) {
// callback()
// }
}
function setTotal(total: number) {
totalRef.value = total;
}
function setPageSize(pageSize: number):void {
pageSizeRef.value = pageSize;
}
function changePage(flag: 'next'|'prev'):void {
if(flag === 'next') {
if (currentRef.value >= pagesRef.value) return;
else {
setCurrentPage(currentRef.value + 1)
}
}else if(flag === 'prev') {
if (currentRef.value <= 1) return;
else {
currentRef.value --;
setCurrentPage(currentRef.value - 1)
}
}
}
return { setCurrentPage,setPageSize, changePage, current: currentRef, limit, total:totalRef, setTotal,pagesRef };
}

@ -0,0 +1,65 @@
import type { Ref } from 'vue';
import { ref, onMounted, watch, onUnmounted } from 'vue';
import { isWindow, isObject } from '~//utils/is';
import { useThrottleFn } from '@vueuse/core';
export function useScroll(
refEl: Ref<Element | Window | null>,
options?: {
wait?: number;
leading?: boolean;
trailing?: boolean;
},
) {
const refX = ref(0);
const refY = ref(0);
let handler = () => {
if (isWindow(refEl.value)) {
refX.value = refEl.value.scrollX;
refY.value = refEl.value.scrollY;
} else if (refEl.value) {
refX.value = (refEl.value as Element).scrollLeft;
refY.value = (refEl.value as Element).scrollTop;
}
};
if (isObject(options)) {
let wait = 0;
if (options.wait && options.wait > 0) {
wait = options.wait;
Reflect.deleteProperty(options, 'wait');
}
handler = useThrottleFn(handler, wait);
}
let stopWatch: () => void;
onMounted(() => {
stopWatch = watch(
refEl,
(el, prevEl, onCleanup) => {
if (el) {
el.addEventListener('scroll', handler);
} else if (prevEl) {
prevEl.removeEventListener('scroll', handler);
}
onCleanup(() => {
refX.value = refY.value = 0;
el && el.removeEventListener('scroll', handler);
});
},
{ immediate: true },
);
});
onUnmounted(() => {
refEl.value && refEl.value.removeEventListener('scroll', handler);
});
function stop() {
stopWatch && stopWatch();
}
return { refX, refY, stop };
}

@ -0,0 +1,59 @@
import { isFunction, isUnDef } from '~~/utils/is';
import { ref, unref } from 'vue';
export interface ScrollToParams {
el: any;
to: number;
duration?: number;
callback?: () => any;
}
const easeInOutQuad = (t: number, b: number, c: number, d: number) => {
t /= d / 2;
if (t < 1) {
return (c / 2) * t * t + b;
}
t--;
return (-c / 2) * (t * (t - 2) - 1) + b;
};
const move = (el: HTMLElement, amount: number) => {
el.scrollTop = amount;
};
const position = (el: HTMLElement) => {
return el.scrollTop;
};
export function useScrollTo({ el, to, duration = 500, callback }: ScrollToParams) {
const isActiveRef = ref(false);
const start = position(el);
const change = to - start;
const increment = 20;
let currentTime = 0;
duration = isUnDef(duration) ? 500 : duration;
const animateScroll = function () {
if (!unref(isActiveRef)) {
return;
}
currentTime += increment;
const val = easeInOutQuad(currentTime, start, change, duration);
move(el, val);
if (currentTime < duration && unref(isActiveRef)) {
requestAnimationFrame(animateScroll);
} else {
if (callback && isFunction(callback)) {
callback();
}
}
};
const run = () => {
isActiveRef.value = true;
animateScroll();
};
const stop = () => {
isActiveRef.value = false;
};
return { start: run, stop };
}

@ -0,0 +1,17 @@
<template>
<Header />
<BannerSearch v-if="store.getMenuTheme === 'dark'" />
<BannerSimple :title="store.getBannerTitle" v-else />
<!-- <div :style="`transition:margin .2s;height: 500px;background-color: tan;margin-top: ${store.getMenuTheme === 'dark' ? '-100px': 0}`"></div>-->
<slot />
<Footer />
</template>
<script setup>
const store = useAppStore()
console.log('store>>>', store.getMenuTheme)
// console.log()
</script>
<style scoped></style>

@ -0,0 +1,52 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
css: ['element-plus/dist/index.css'],
imports: {
dirs: ['@/stores', '@/composables'],
},
// @ts-ignore
modules: [
'@pinia/nuxt',
'@nuxtjs/tailwindcss',
'@element-plus/nuxt',
],
// routeRules: {
// "/": { static: true }, // ssr
// "/about": { static: false }, // spa 应用
// // 3.0.0-12rc -> NetLify
// "/case": { static: true }, // 只会在构建时生成一次静态页面
// "/dynamicInfo": { static: true }, // 只会生成多次静态页面( 会自动重新验证页面时候需要重新生成 )
// "/earlyWarningInfo": { static: true }, // 只会生成多次静态页面( 会自动重新验证页面时候需要重新生成 )
// "/inform": { static: true }, // 只会生成多次静态页面( 会自动重新验证页面时候需要重新生成 )
// "/informTalk": { static: true }, // 只会生成多次静态页面( 会自动重新验证页面时候需要重新生成 )
// "/regulatoryStandards.vue": { static: true }, // 只会生成多次静态页面( 会自动重新验证页面时候需要重新生成 )
// },
elementPlus: {
icon: 'ElIcon',
importStyle: 'scss',
},
vite: {
css: {
preprocessorOptions: {
scss: {
additionalData: '@use "@/assets/style/config.scss" as *;'
}
},
},
},
nitro: {
devProxy: {
"/api": {
target: 'http://research.mcnetmart.com/api',
prependPath: true,
changeOrigin: true,
},
"/uploads": {
target: 'http://research.mcnetmart.com/uploads',
prependPath: true,
changeOrigin: true,
}
}
}
})

@ -0,0 +1,31 @@
{
"private": true,
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"devDependencies": {
"@element-plus/nuxt": "^1.0.3",
"@nuxtjs/tailwindcss": "^6.4.1",
"less": "^4.1.3",
"less-loader": "^11.1.0",
"nuxt": "^3.2.3"
},
"dependencies": {
"@pinia/nuxt": "^0.4.7",
"@types/axios": "^0.14.0",
"@types/crypto-js": "^4.1.1",
"@types/qs": "^6.9.7",
"axios": "^1.3.4",
"crypto-js": "^4.1.1",
"element-plus": "^2.2.35",
"lodash-es": "^4.17.21",
"pinia": "^2.0.33",
"qs": "^6.11.1",
"sass": "^1.58.3",
"vue-i18n": "^9.2.2"
}
}

@ -0,0 +1,40 @@
<template>
<div :class="[prefixCls, 'flex full relative']">
<div :class="`${prefixCls}-content pt-24`">
<el-breadcrumb style="position: absolute; top: 80px;left: 120px" :separator-icon="ArrowRight">
<el-breadcrumb-item class="text-2xl" :to="{ path: '/case' }">案例列表</el-breadcrumb-item>
<el-breadcrumb-item class="text-2xl">案例详情</el-breadcrumb-item>
</el-breadcrumb>
<HtmlPreview :content="content" />
</div>
</div>
</template>
<script setup lang="ts">
definePageMeta({
name: '案例详情',
hidden: true,
headerHost: false,
parentPath: '/case',
// @ts-ignore
path: '/case/:id'
})
useHead({
title: '案例详情'
})
import { ArrowRight } from '@element-plus/icons-vue'
useAppStore().setBannerTitle("案例详情")
const {prefixCls} = useDesign('mai-wrap');
const route = useRoute()
const content = ref('')
async function getData() {
const data = await useApi().caseApi.getCaseInfo(route.params.id)
console.log('data.data.info')
content.value = data.data.info.content;
}
getData()
</script>
<style scoped></style>

@ -0,0 +1,40 @@
<template>
<div :class="[prefixCls, 'flex full relative']">
<div :class="`${prefixCls}-content pt-24`">
<el-breadcrumb style="position: absolute; top: 80px;left: 120px" :separator-icon="ArrowRight">
<el-breadcrumb-item class="text-2xl" :to="{ path: '/inform' }">动态资讯</el-breadcrumb-item>
<el-breadcrumb-item class="text-2xl">动态详情</el-breadcrumb-item>
</el-breadcrumb>
<HtmlPreview :content="content" />
</div>
</div>
</template>
<script setup lang="ts">
definePageMeta({
name:'动态详情',
hidden: true,
headerHost: false,
parentPath: '/inform',
// @ts-ignore
path: '/inform/:id'
})
useHead({
title:'动态详情'
})
import { ArrowRight } from '@element-plus/icons-vue'
useAppStore().setBannerTitle("动态详情")
const {prefixCls} = useDesign('mai-wrap');
const route = useRoute()
const content = ref('')
async function getData() {
const data = await useApi().informationApi.getInformationDetail(route.params.id)
console.log('data.data.info')
content.value = data.data.info.content;
}
getData()
</script>
<style scoped></style>

@ -0,0 +1,40 @@
<template>
<div :class="[prefixCls, 'flex full relative']">
<div :class="`${prefixCls}-content pt-24`">
<el-breadcrumb style="position: absolute; top: 80px;left: 120px" :separator-icon="ArrowRight">
<el-breadcrumb-item class="text-2xl" :to="{ path: '/rules' }">法律法规</el-breadcrumb-item>
<el-breadcrumb-item class="text-2xl">法律法规详情</el-breadcrumb-item>
</el-breadcrumb>
<HtmlPreview :content="content" />
</div>
</div>
</template>
<script setup lang="ts">
definePageMeta({
name:'法律法规详情',
hidden: true,
headerHost: false,
parentPath: '/rules',
// @ts-ignore
path: '/rules/:id'
})
useHead({
title:'法律法规详情'
})
import { ArrowRight } from '@element-plus/icons-vue'
useAppStore().setBannerTitle("法律法规详情")
const {prefixCls} = useDesign('mai-wrap');
const route = useRoute()
const content = ref('')
async function getData() {
const data = await useApi().technicalApi.getTechnicalDetail(route.params.id)
console.log('data.data.info')
content.value = data.data.info.content;
}
getData()
</script>
<style scoped></style>

@ -0,0 +1,40 @@
<template>
<div :class="[prefixCls, 'flex full relative']">
<div :class="`${prefixCls}-content pt-24`">
<el-breadcrumb style="position: absolute; top: 80px;left: 120px" :separator-icon="ArrowRight">
<el-breadcrumb-item class="text-2xl" :to="{ path: '/warning' }">预警信息</el-breadcrumb-item>
<el-breadcrumb-item class="text-2xl">预警详情</el-breadcrumb-item>
</el-breadcrumb>
<HtmlPreview :content="content" />
</div>
</div>
</template>
<script setup lang="ts">
definePageMeta({
name: '预警详情',
hidden: true,
headerHost: false,
parentPath: '/warning',
// @ts-ignore
path: '/warning/:id'
})
useHead({
title:'预警详情'
})
import { ArrowRight } from '@element-plus/icons-vue'
useAppStore().setBannerTitle("预警详情")
const {prefixCls} = useDesign('mai-wrap');
const route = useRoute()
const content = ref('')
async function getData() {
const data = await useApi().warningApi.getWarningDetail(route.params.id)
console.log('data.data.info')
content.value = data.data.info.content;
}
getData()
</script>
<style scoped></style>

@ -0,0 +1,57 @@
<template>
<div :class="[prefixCls, 'flex']">
<mai-tab :active="current.id" :data-source="tabs" @change="handleChange"/>
<div :class="`${prefixCls}-content`">
<h2>湛江水产技贸研究评议基地</h2>
<HtmlPreview :content="content"/>
</div>
</div>
</template>
<script setup lang="ts">
definePageMeta({
name: '关于我们',
headerHost: false,
order: 8,
})
useHead({
title:'关于我们'
})
import {randomUUID} from '~~/utils';
const {aboutApi} = useApi()
const {prefixCls} = useDesign('mai-wrap');
const tabs = ref([
{
name: '关于我们',
id: randomUUID(),
children: [
{name: '关于我们', id: randomUUID()},
{name: '联系我们', id: randomUUID()},
],
},
]);
const content = ref('');
const current = ref(tabs.value[0].children[0]);
const { setBannerTitle } = useAppStore()
async function handleChange(data: any) {
current.value = data;
setBannerTitle(data.name);
if (data.name === '联系我们') {
aboutApi.getContractInfo().then(({data}) => {
// console.log('res>>>>', res);
content.value = data.content;
});
} else {
aboutApi.getAboutInfo().then(({data}) => {
content.value = data.content;
});
}
}
handleChange(tabs.value[0].children[0]);
setBannerTitle(tabs.value[0].children[0].name)
</script>
<style scoped lang="less"></style>

@ -0,0 +1,122 @@
<template>
<div :class="[prefixCls, 'flex full']">
<div :class="`${prefixCls}-content`">
<el-skeleton class="flex flex-wrap justify-between" :loading="loading" animated :count="6">
<template #template>
<div style="width: 32%;">
<el-skeleton-item
variant="image"
style="width: 400px; height: 267px;"
/>
<div style="padding: 14px;">
<el-skeleton-item variant="h3"
style="width: 100%; height: 30px;margin-bottom: 15px;"/>
<div>
<el-skeleton-item variant="text"
style="width: 30%;height: 40px; border-radius: 25px"/>
</div>
</div>
</div>
</template>
<template #default>
<el-row :gutter="24" v-if="content.data && content.data.length">
<el-col
v-for="(item, index) in content.data"
:key="index"
:span="8"
class="mb-6"
>
<el-card :body-style="{ padding: '0px' }">
<el-image style="width: 100%; height: 200px" :src="item.image" :fit="'cover'"/>
<div style="padding: 14px">
<h4 class="text-2xl mb-6 text-ellipsis overflow-hidden"
style=" line-height:30px;height: 30px;display:inline-block">{{
item.title
}}</h4>
<div class="bottom">
<el-button round
style="font-size: 18px; color: #022950; border-color: #022950;"><nuxt-link :to="`/case/${item.id}`">查看详情</nuxt-link>
</el-button>
</div>
</div>
</el-card>
</el-col>
</el-row>
<div v-else>
<el-empty description="暂无数据"/>
</div>
</template>
</el-skeleton>
<div class="mt-32">
<Pagination
:current="current"
:limit="limit"
:pages="pagesRef"
:total="total"
@change="onChangePage"
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {useThrottleFn} from "@vueuse/core";
definePageMeta({
name: '案例列表',
headerHost: true,
order: 6,
})
useHead({
title:'案例列表'
})
const {prefixCls} = useDesign('mai-wrap');
const {commonApi} = useApi();
const pagination = reactive({
current: 1,
limit: 6,
keyword: '',
total: 0
})
const content = ref([]);
const loading = ref(true);
const {setTotal, current, total, setCurrentPage, limit, pagesRef} = usePagination(1, 6, 0)
async function getData() {
loading.value = true;
console.log('limit: >>>>>', limit)
const {data} = await commonApi.getCaseList({page: current.value, limit: limit});
content.value = data;
setTotal(data.total);
if (data) {
loading.value = false;
}
}
function onChangePage(val: number) {
setCurrentPage(val)
useThrottleFn(getData, 1000)()
}
getData()
</script>
<style scoped lang="scss">
:deep(.el-pagination) {
.el-pager {
.number {
margin: 0 5px;
}
.number.is-active {
background-color: #022950;
@apply text-base;
}
}
}
</style>

@ -0,0 +1,286 @@
<template>
<div :class="[prefixCls, 'flex']">
<mai-tab :active="current.id" :data-source="tabs" @change="handleChange"/>
<div :class="`${prefixCls}-content`">
<h2>{{ current.title }}</h2>
<el-row>
<el-col :span="12">
<el-form v-if="current.type === 1" ref="form" :model="formData" :rules="rules1"
label-position="left"
label-width="120px">
<el-form-item prop="country" label="通报国家">
<el-input v-model="formData.country" placeholder="全部"/>
</el-form-item>
<el-form-item prop="number" label="通报号">
<el-input v-model="formData.number" placeholder="请输入通报号"/>
</el-form-item>
<el-form-item prop="title" label="通报标题">
<el-input v-model="formData.title" placeholder="请输入通报标题"/>
</el-form-item>
<el-form-item prop="content" label="评议意见">
<el-input v-model="formData.content" type="textarea" row="5"
placeholder="请输入评议意见"/>
</el-form-item>
<el-form-item label="附件">
<el-upload
v-model:file-list="formData.fileList"
action="/api/common/upload"
:http-request="handleUpload"
accept=".jpg,.png,.bmp,.jpeg,.gif,.webp,.zip,.rar,.wav,.mp4,.mp3,.webm,.pdf"
>
<div class="mai-upload">
<el-icon size="68">
<el-icon-plus></el-icon-plus>
</el-icon>
<div style="margin-top: 8px">点击上传</div>
</div>
</el-upload>
</el-form-item>
<el-form-item prop="author" label="提交人">
<el-input v-model="formData.author" placeholder="请输入提交人"/>
</el-form-item>
<el-form-item prop="contact" label="联系方式">
<el-input v-model="formData.contact" placeholder="请输入联系方式"/>
</el-form-item>
<el-form-item label-width="500px">
<el-button @click="handleSubmit" type="primary">提交</el-button>
<!-- <el-button style="margin-left: 10px">关闭</el-button>-->
</el-form-item>
</el-form>
<el-form v-else ref="form" :model="consultFormData" :rules="rules2" label-position="left"
label-width="120px">
<el-form-item prop="title" label="标题">
<el-input v-model="consultFormData.title" placeholder="请输入标题"/>
</el-form-item>
<el-form-item prop="content" label="内容">
<el-input v-model="consultFormData.content" type="textarea" placeholder="请输入内容"/>
</el-form-item>
<el-form-item label="附件">
<el-upload
v-model:file-list="consultFormData.fileList"
action="/api/common/upload"
:http-request="handleUpload"
accept=".jpg,.png,.bmp,.jpeg,.gif,.webp,.zip,.rar,.wav,.mp4,.mp3,.webm,.pdf"
>
<div class="mai-upload">
<el-icon size="68">
<el-icon-plus></el-icon-plus>
</el-icon>
<div style="margin-top: 8px">点击上传</div>
</div>
</el-upload>
</el-form-item>
<el-form-item prop="author" label="提交人">
<el-input v-model="consultFormData.author" placeholder="请输入提交人"/>
</el-form-item>
<el-form-item prop="contact" label="联系方式">
<el-input v-model="consultFormData.contact" placeholder="请输入联系方式"/>
</el-form-item>
<el-form-item label-width="500px">
<el-button @click="handleSubmit" type="primary">提交</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</div>
</template>
<script setup>
import {randomUUID} from '~~/utils';
definePageMeta({
name: '通报评议',
headerHost: false,
order: 7,
})
useHead({
title:'通报评议'
})
const {prefixCls} = useDesign('mai-wrap');
const {setBannerTitle} = useAppStore();
const {commonApi, commentApi} = useApi()
const tabs = ref([
{
name: '通报评议',
id: randomUUID(),
children: [
{name: "我要评议", id: randomUUID(), type: 1},
{name: "我要咨询", id: randomUUID(), type: 2},
],
},
]);
const formData = ref({
fileList: [],
country: '',
number: '',
title: '',
content: '',
attachfile: '',
author: '',
contact: '',
});
const consultFormData = ref({
fileList: [],
title: '',
content: '',
attachfile: '',
author: '',
contact: '',
})
const rules1 = ref({
country: {message: '请输入通报国家', required: true, trigger: 'blur'},
number: {message: '请输入通报号', required: true, trigger: 'blur'},
title: {message: '请输入通报标题', required: true, trigger: 'blur'},
content: {message: '请输入评议意见', required: true, trigger: 'blur'},
author: {message: '请输入提交人', required: true, trigger: 'blur'},
contact: {message: '请输入联系方式', required: true, trigger: 'blur'},
})
const rules2 = ref({
title: {message: '请输入标题', required: true, trigger: 'blur'},
content: {message: '请输入意见', required: true, trigger: 'blur'},
author: {message: '请输入提交人', required: true, trigger: 'blur'},
contact: {message: '请输入联系方式', required: true, trigger: 'blur'},
})
const form = ref(null);
const current = ref(tabs.value[0].children[0]);
console.log('_tabs[0].children[0].id>>>', tabs.value[0].children[0].id);
function handleChange(data) {
current.value = data;
setBannerTitle(data.title);
}
handleChange(tabs.value[0].children[0]);
function handleUpload(options) {
if (current.value.type === 1) {
const index = formData.value.fileList.findIndex(i => i.uid === options.file.uid);
commonApi.handleUpload(options.file).then(res => {
formData.value.fileList.splice(index, 1, {
...formData.value.fileList[0],
url: res.data.fullurl
})
}).catch(res => {
if (!res.data) {
formData.value.fileList.splice(index, 1)
}
})
}else {
const index = consultFormData.value.fileList.findIndex(i => i.uid === options.file.uid);
commonApi.handleUpload(options.file).then(res => {
consultFormData.value.fileList.splice(index, 1, {
...consultFormData.value.fileList[0],
url: res.data.fullurl
})
}).catch(res => {
if (!res.data) {
consultFormData.value.fileList.splice(index, 1)
}
})
}
}
function handleSubmit() {
form.value.validate((val) => {
if (val) {
console.log(formData.value)
console.log('val: >>>>>', val)
const origin = import.meta.env.VITE_GLOB_ORIGIN
if (current.value.type === 1) {
const params = Object.assign({}, formData.value)
params.attachfile = params.fileList.map(it=> it.url.replace(origin, '')).join(',')
delete params.fileList;
commentApi.submitComment(params).then(()=> {ElMessage.success('提交成功'); form.value.resetFields(); formData.value.fileList = []})
}else {
const params = Object.assign({}, consultFormData.value)
params.attachfile = params.fileList.map(it=> it.url.replace(origin, '')).join(',')
delete params.fileList;
commentApi.submitConsult(params).then(()=> {ElMessage.success('提交成功'); form.value.resetFields(); consultFormData.value.fileList = []})
}
}
})
}
</script>
<style scoped lang="scss">
$prefix-cls: '#{$name-prefix}-mai-wrap';
.#{$prefix-cls} {
:deep(.el-form) {
padding: 20px 0 0 20px;
margin: -36px 0 0 -20px;
border-left: 1px solid #ddd;
.el-form-item__label {
font-size: 20px;
height: 60px;
line-height: 60px;
}
.el-input__wrapper, .el-textarea {
height: 60px;
font-size: 20px;
//border: 1px solid #02294F;
border-radius: 10px;
box-shadow: 0 0 0 1px #02294F;
//&:focus{;}
}
.is-error .el-input__wrapper,
.is-error .el-textarea {
box-shadow: 0 0 0 1px var(--el-color-danger);
}
.el-textarea, .el-textarea__inner {
//box-shadow: unset;
height: 300px;
border-radius: 10px;
overflow: hidden;
resize: none;
}
.el-upload-list-picture-card-container {
width: 150px;
height: 150px;
}
.el-upload {
border: unset;
width: 150px;
height: 150px;
}
.el-button {
width: 150px;
height: 60px;
font-size: 20px;
border-radius: 10px;
&--primary {
background: #022950;
}
}
.mai-upload {
width: 100%;
height: 100%;
border-radius: 8px;
border: 1px solid #022950;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
user-select: none;
font-size: 16px;
color: #9a9a9a;
.anticon {
font-size: 60px !important;
}
}
}
}
</style>

@ -0,0 +1,207 @@
<template>
<div class="mai-ocr-wrap flex justify-center">
<div class="mai-ocr-item flex">智能OCR</div>
<div class="mai-ocr-item flex">识别</div>
</div>
<div :class="classList.news">
<CommonTitle title="热门资讯"/>
<el-skeleton :key="1" :loading="hotInformLoading" animated
style="margin-bottom: 80px;height: 520px">
<template #template>
<div class="flex justify-between">
<el-skeleton-item style="width: 785px;height:520px" variant="image"></el-skeleton-item>
<div style="width: 48%" class="flex-col flex justify-between">
<el-skeleton-item variant="text" style="height:100px"></el-skeleton-item>
<el-skeleton-item variant="text" style="height:100px"></el-skeleton-item>
<el-skeleton-item variant="text" style="height:100px"></el-skeleton-item>
<el-skeleton-item variant="text" style="height:100px"></el-skeleton-item>
</div>
</div>
</template>
<template #default>
<div class="home-news flex">
<div class="media-img">
<img
style="object-fit: cover"
:src="newsHilight.image" alt=""
/>
</div>
<div class="newsList">
<div
class="news-item flex items-center justify-between"
v-for="item in newsList"
:key="item.id"
@mouseenter="changeCurrentData(item)"
>
<div class="flex items-center c-item">
<div class="date">
<span class="month">{{ dayjs(item.updatetime * 1000).format('MM/DD') }}</span>
<span class="year">{{ dayjs(item.updatetime * 1000).format('YYYY') }}</span>
</div>
<el-divider class="divider" direction="vertical"/>
<nuxt-link class="new-title text-ellipsis" :to="`/inform/${item.id}`">{{ item.title }}
</nuxt-link>
</div>
<nuxt-link :to="`/inform/${item.id}`">
<img class="open-new-btn" src="~/assets/images/home/go.png" alt=""/>
</nuxt-link>
</div>
</div>
</div>
</template>
</el-skeleton>
<MoreBtn url="/inform"/>
</div>
<div :class="classList.inform">
<CommonTitle title="TBT/SPS通报"/>
<el-skeleton :loading="hotTbtLoading" animated :count="2">
<template #template>
<div class="flex el-skeleton justify-between flex-wrap w-full mb-4">
<el-skeleton-item variant="text" style="height:220px; width: 32%"></el-skeleton-item>
<el-skeleton-item variant="text" style="height:220px; width: 32%"></el-skeleton-item>
<el-skeleton-item variant="text" style="height:220px; width: 32%"></el-skeleton-item>
</div>
</template>
<template #default>
<div class="home-inform flex flex-wrap">
<div class="inform-item flex flex-col" v-for="item in informList" :key="item.id">
<h3>{{ item.notification_title }}</h3>
<p>{{ item.product }}</p>
<span>{{ item.notification_time_text }}</span>
</div>
</div>
</template>
</el-skeleton>
<MoreBtn url="/tptsps"/>
</div>
<div :class="classList.rule">
<CommonTitle title="法律法规"/>
<el-skeleton :loading="hotRuleLoading" style="width:100%" animated :count="3">
<template #template>
<div class="flex el-skeleton justify-between flex-wrap w-full mb-4">
<el-skeleton-item variant="text" style="height:100px; width: 49.5%"></el-skeleton-item>
<el-skeleton-item variant="text" style="height:100px; width: 49.5%"></el-skeleton-item>
</div>
</template>
<template #default>
<div class="home-rule mb-11 flex flex-wrap justify-between">
<div class="rule-item" v-for="item in ruleList" :key="item.id">
<nuxt-link :to="`/rules/${item.id}`">
<div class="w-full h-full flex items-center">
<div class="date">
<span class="month">{{ item.month }}</span>
<span class="year">{{ item.year }}</span>
</div>
<div class="title">{{ item.title }}</div>
</div>
</nuxt-link>
</div>
</div>
</template>
</el-skeleton>
<MoreBtn url="/rules"/>
</div>
<div :class="classList.case">
<CommonTitle title="案例展示"/>
<el-skeleton :loading="hostCaseLoading" class="flex justify-between" animated
style="width: 100%" :count="3">
<template #template>
<div class="w-1/3 mx-2 flex flex-col">
<el-skeleton-item variant="image"
style="width: 100%; height: 447px; margin-bottom: 20px"></el-skeleton-item>
<el-skeleton-item variant="text"
style="height: 30px; margin-bottom: 20px"></el-skeleton-item>
<el-skeleton-item variant="button"></el-skeleton-item>
</div>
</template>
<template #default>
<div class="home-case flex flex-wrap">
<div class="case-item flex flex-col" v-for="item in caseList" :key="item.id">
<el-image class="img" :fit="'cover'" :src="item.image" alt=""/>
<p class="text-ellipsis w-full overflow-hidden"
style="white-space:nowrap;height: 30px; line-height: 30px">{{ item.title }}</p>
<button class="more">
<nuxt-link :to="`/case/${item.id}`">立即查看</nuxt-link>
</button>
</div>
</div>
</template>
</el-skeleton>
<MoreBtn url="/case"/>
</div>
</template>
<script setup lang="ts">
import {InformationDto} from "~~/api/types";
import dayjs from 'dayjs';
definePageMeta({
name: '首页',
headerHost: true,
order: 1,
})
const hotInformLoading = ref(true)
const hotTbtLoading = ref(true)
const hotRuleLoading = ref(true)
const hostCaseLoading = ref(true)
const {prefixCls: prefixNews} = useDesign('news-wrap');
const {prefixCls: prefixInform} = useDesign('inform-wrap');
const {prefixCls: prefixRule} = useDesign('rule-wrap');
const {prefixCls: prefixCase} = useDesign('case-wrap');
useHead({
title:'首页'
})
const classList = reactive({
news: [`${prefixNews} mai-section`],
inform: [`${prefixInform} mai-section`],
rule: [`${prefixRule} mai-section`],
case: [`${prefixCase} mai-section`],
});
const newsList = ref();
const informList = ref();
const ruleList = ref();
const caseList = ref();
const newsHilight = ref<InformationDto>({
id: '',
title: '',
})
async function getData() {
const {homeApi} = useApi()
hotInformLoading.value = true;
hotTbtLoading.value = true;
hotRuleLoading.value = true;
hostCaseLoading.value = true;
const {data: hotInformation} = await homeApi.getHotNewsInfo(1, 4)
const {data: hotTechnical} = await homeApi.getHotTechnical(1, 6)
const {data: hotCase} = await homeApi.getHotCase(1, 3)
const {data: hotTs} = await homeApi.getHotTs(1, 6)
newsList.value = hotInformation?.data;
informList.value = hotTs?.data
ruleList.value = hotTechnical?.data
caseList.value = hotCase?.data
if (newsList.value.length) {
changeCurrentData(newsList.value[0])
}
hotInformLoading.value = false;
hotTbtLoading.value = false;
hotRuleLoading.value = false;
hostCaseLoading.value = false;
console.log('hotInformation>>>>', hotInformation)
}
function changeCurrentData(data: InformationDto) {
console.log('data.image>>>>', data.image)
newsHilight.value = Object.assign({}, data);
newsHilight.value.image = import.meta.env.VITE_GLOB_ORIGIN + newsHilight.value.image
}
getData()
</script>
<style lang="scss" scoped>
@import '~~/assets/style/home.scss';
</style>

@ -0,0 +1,109 @@
<template>
<div :class="[prefixCls, 'flex']">
<div>
<mai-tab :active="currentTab.id" :data-source="tabs" @change="handleChange"/>
</div>
<div :class="`${prefixCls}-content dynamicInfo flex flex-col px-8`" style="border-left: 1px solid #d9d9d9;">
<div class="mb-10">
<el-skeleton :loading="loading" animated :count="4">
<template #template>
<div class="flex flex-col mb-15">
<el-skeleton-item variant="text" style="width: 500px;height: 50px; margin-bottom: 25px"></el-skeleton-item>
<el-skeleton-item variant="text" style="width: 100%;height: 30px; margin-bottom: 20px"></el-skeleton-item>
<el-skeleton-item variant="text" style="width: 100%;height: 30px; margin-bottom: 30px"></el-skeleton-item>
<el-skeleton-item variant="text" style="width: 200px;height: 20px; margin-bottom: 20px"></el-skeleton-item>
</div>
</template>
<template #default>
<div v-if="content.length" class="py-8 cursor-pointer select-none" style="border-bottom: 1px solid #d9d9d9;" v-for="item in content" :key="item.id">
<nuxt-link :to="`/inform/${item.id}`">
<h3 class="text-2xl mb-6 w-full overflow-hidden text-ellipsis">{{ item.title }}</h3>
<p class="text-xl text-zinc-500 mb-6">{{ item.description }}</p>
<p class="">{{ dayjs(item.createtime * 1000).format('YYYY-MM-DD hh:mm:ss') }}</p>
</nuxt-link>
</div>
<div v-else>
<el-empty description="暂无数据" />
</div>
</template>
</el-skeleton>
</div>
<Pagination
:current="current"
:total="total"
:limit="limit"
:pages="pagesRef"
@change="onChangePage"
/>
</div>
</div>
</template>
<script setup lang="ts">
import dayjs from "dayjs";
definePageMeta({
name: '动态资讯',
headerHost: true,
order: 2,
})
useHead({
title:'动态资讯'
})
import {randomUUID} from '~~/utils';
const { informationApi } = useApi()
const {prefixCls} = useDesign('mai-wrap');
const {setTotal, current, total, setCurrentPage, limit, pagesRef} = usePagination(1, 4, 0)
const tabs = ref([
{
name: '动态资讯',
id: randomUUID(),
children: [
{name: 'WTO资讯', id: randomUUID()},
{name: '国内资讯', id: randomUUID()},
{name: '国外资讯', id: randomUUID()},
],
},
]);
const currentTab = ref({});
const content = ref([]);
const loading = ref(true);
const searchData = ref({})
async function handleChange(data:any) {
currentTab.value = data;
setCurrentPage(1)
await getList(data)
}
function onChangePage(val: number) {
setCurrentPage(val)
getList(currentTab.value)
}
async function getList(data: any) {
if(data) {
loading.value = true;
const {data:listDatas} = await informationApi.getInformationList({...searchData.value,page: current.value, limit: limit, category_id: data.id})
content.value = listDatas.data;
loading.value = false;
setTotal(listDatas.total)
}
}
async function getData() {
const cate = await informationApi.getInformationCate()
tabs.value[0].children = cate.data
await handleChange(cate.data[0]);
}
getData()
</script>
<style scoped lang="less">
.dynamicInfo {
margin-top: 0;
padding-bottom: 100px;
}
</style>

@ -0,0 +1,86 @@
<template>
<div :class="[prefixCls, 'flex']">
<div>
<mai-tab :active="currentTab.id" :data-source="tabs" @change="handleChange"/>
</div>
<div :class="`${prefixCls}-content dynamicInfo flex flex-col px-8`" style="border-left: 1px solid #d9d9d9;">
<div class="py-8 cursor-pointer select-none" style="border-bottom: 1px solid #d9d9d9;" v-for="item in content" :key="item.id">
<nuxt-link :to="`/rules/${item.id}`">
<h3 class="text-2xl mb-6 w-full overflow-hidden text-ellipsis">{{ item.title }}</h3>
<p class="text-xl text-zinc-500 mb-6">{{ item.description }}</p>
<p class="">{{ dayjs(item.createtime * 1000).format('YYYY-MM-DD hh:mm:ss') }}</p>
</nuxt-link>
</div>
<Pagination
:current="current"
:total="total"
:limit="limit"
:pages="pagesRef"
@change="onChangePage"
/>
</div>
</div>
</template>
<script setup lang="ts">
import dayjs from "dayjs";
definePageMeta({
name: '法规标准',
headerHost: true,
order: 2,
})
useHead({
title:'法规标准'
})
import {randomUUID} from '~~/utils';
const {setTotal, current, total, setCurrentPage, limit, pagesRef} = usePagination(1, 4, 0)
const { technicalApi } = useApi()
const {prefixCls} = useDesign('mai-wrap');
const tabs = ref([
{
name: '法规标准',
id: randomUUID(),
children: [],
},
]);
const currentTab = ref({});
const content = ref([]);
const searchData = ref({})
async function handleChange(data:any) {
currentTab.value = data;
setCurrentPage(1)
await getList(data)
}
function onChangePage(val: number) {
setCurrentPage(val)
getList(currentTab.value)
}
async function getList(data: any) {
if(data) {
// loading.value = true;
const {data:listDatas} = await technicalApi.getTechnicalList({...searchData.value,page: current.value, limit: limit, category_id: data.id})
content.value = listDatas.data;
// loading.value = false;
setTotal(listDatas.total)
}
}
async function getData() {
const cate = await technicalApi.getTechnicalCate()
tabs.value[0].children = cate.data
await handleChange(cate.data[0]);
}
getData()
</script>
<style scoped lang="less">
.dynamicInfo {
margin-top: 0;
padding-bottom: 100px;
}
</style>

@ -0,0 +1,197 @@
<template>
<div :class="[prefixCls, 'flex']">
<div>
<mai-tab :active="currentTab.id" :data-source="tabs" @change="handleChange"/>
</div>
<div :class="`${prefixCls}-content dynamicInfo flex flex-col pl-8`"
style="border-left: 1px solid #d9d9d9;">
<el-form size="large" inline>
<el-form-item label="关键字">
<el-input v-model="searchData.keyword" placeholder="请输入关键字"/>
</el-form-item>
<el-form-item label="覆盖产品">
<el-input v-model="searchData.product" placeholder="请输入覆盖产品"/>
</el-form-item>
<el-form-item label="通报成员">
<el-input v-model="searchData.notifying_members" placeholder="请输入通报成员"/>
</el-form-item>
<el-row>
<el-col :span="10">
<el-form-item label="通报起止时间">
<el-date-picker type="daterange" v-model="searchData.notification_time" placeholder="请选择日期"/>
</el-form-item>
</el-col>
<el-col :span="8" :offset="6" class="flex justify-end">
<el-form-item style="margin-right: 0;">
<el-button type="primary" @click="getList(currentTab)"></el-button>
<el-button @click="resetData"></el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-table v-loading="loading" size="large" border :data="content" :empty-text="'暂无数据'"
:header-cell-style="{background: '#f5f5f5', color:'#333333'}">
<el-table-column show-overflow-tooltip align="center" label="通报号"
prop="notification_number"></el-table-column>
<el-table-column show-overflow-tooltip align="center" label="通报标题"
prop="notification_title">
<template #header>
<span class="text-red-500">通报标题</span>
</template>
</el-table-column>
<el-table-column show-overflow-tooltip align="center" label="通报成员"
prop="notifying_members"></el-table-column>
<el-table-column show-overflow-tooltip align="center" label="通报时间"
prop="notification_time_text"></el-table-column>
</el-table>
<div class="mt-32">
<Pagination
:current="current"
:limit="limit"
:pages="pagesRef"
:total="total"
@change="onChangePage"
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {randomUUID} from '~~/utils';
import dayjs from "dayjs";
import {useThrottleFn} from "@vueuse/core";
definePageMeta({
name: 'TBT/SPS通报',
headerHost: false,
order: 5,
})
useHead({
title: 'TBT/SPS通报'
})
const {setBannerTitle} = useAppStore()
const {tbspApi} = useApi()
const {prefixCls} = useDesign('mai-wrap');
setBannerTitle('TBT/SPS通报')
const tabs = ref([
{
name: 'TBT通报查询',
id: randomUUID(),
children: [],
},
{
name: 'SPS通报查询',
id: randomUUID(),
children: [],
},
]);
const currentTab = ref({});
const content = ref([]);
const loading = ref(true);
const {setTotal, current, total, setCurrentPage, limit, pagesRef} = usePagination(1, 6, 0)
const searchData = ref({
notifying_members: '',
notification_time: '',
keyword: '',
product: '',
})
function resetData(flag: boolean = true) {
searchData.value = {
notifying_members: '',
notification_time: '',
keyword: '',
product: ''
}
flag && getList(currentTab.value)
}
function onChangePage(val: number) {
setCurrentPage(val)
getList(currentTab.value)
}
async function handleChange(data: any) {
currentTab.value = data;
setCurrentPage(1)
resetData(false)
await getList(data)
}
async function getList(data: any) {
if (data) {
loading.value = true;
const searchParams = {
...searchData.value,
notification_starttime:'',
notification_endtime: '',
}
if (searchData.value.notification_time) {
searchParams.notification_endtime = dayjs(searchData.value.notification_time[1]).format('YYYY-DD-MM hh:mm:ss')
searchParams.notification_starttime = dayjs(searchData.value.notification_time[0]).format('YYYY-DD-MM hh:mm:ss')
// console.log('search: >>>>>', searchData.value.notification_time)
// @ts-ignore
delete searchParams.notification_time;
}
const {data: listDatas} = await tbspApi.getTsList({
...searchParams,
page: current.value,
limit: limit,
category_id: data.id
})
content.value = listDatas.data;
loading.value = false;
setTotal(listDatas.total)
}
}
async function getData() {
const cate = await tbspApi.getTsTbTypes()
tabs.value[0].children = cate.data.tbt
tabs.value[1].children = cate.data.sps
await handleChange(cate.data.tbt[0]);
}
getData()
</script>
<style scoped lang="scss">
.dynamicInfo {
margin-top: 0;
padding-bottom: 100px;
}
:deep(.el-form) {
.el-form-item__label {
height: 60px;
line-height: 60px !important;
@apply text-xl;
}
.el-input__wrapper {
border-radius: 10px;
height: 60px;
box-shadow: unset;
background-color: #F7F7F7;
@apply text-xl;
}
.el-icon {
font-size: 20px;
}
.el-range-input {
font-size: 20px;
}
.el-button {
width: 150px;
height: 60px;
box-shadow: unset;
@apply text-xl rounded-xl;
&--primary{
background-color: #022950;
}
}
}
</style>

@ -0,0 +1,107 @@
<template>
<div :class="[prefixCls, 'flex']">
<div>
<mai-tab :active="currentTab.id" :data-source="tabs" @change="handleChange"/>
</div>
<div :class="`${prefixCls}-content dynamicInfo flex flex-col px-8`" style="border-left: 1px solid #d9d9d9;">
<div class="mb-10">
<el-skeleton :loading="loading" animated :count="4">
<template #template>
<div class="flex flex-col mb-15">
<el-skeleton-item variant="text" style="width: 500px;height: 50px; margin-bottom: 25px"></el-skeleton-item>
<el-skeleton-item variant="text" style="width: 100%;height: 30px; margin-bottom: 20px"></el-skeleton-item>
<el-skeleton-item variant="text" style="width: 100%;height: 30px; margin-bottom: 30px"></el-skeleton-item>
<el-skeleton-item variant="text" style="width: 200px;height: 20px; margin-bottom: 20px"></el-skeleton-item>
</div>
</template>
<template #default>
<div v-if="content.length" class="py-8 cursor-pointer select-none" style="border-bottom: 1px solid #d9d9d9;" v-for="item in content" :key="item.id">
<nuxt-link :to="`/warning/${item.id}`">
<h3 class="text-2xl mb-6 w-full overflow-hidden text-ellipsis">{{ item.title }}</h3>
<p class="text-xl text-zinc-500 mb-6">{{ item.description }}</p>
<p class="">{{ dayjs(item.createtime * 1000).format('YYYY-MM-DD hh:mm:ss') }}</p>
</nuxt-link>
</div>
<div v-else>
<el-empty description="暂无数据" />
</div>
</template>
</el-skeleton>
</div>
<Pagination
:current="current"
:total="total"
:limit="limit"
:pages="pagesRef"
@change="onChangePage"
/>
</div>
</div>
</template>
<script setup lang="ts">
import dayjs from "dayjs";
import {randomUUID} from '~~/utils';
definePageMeta({
name: '预警信息',
headerHost: true,
order: 3,
})
useHead({
title:'预警信息'
})
const { warningApi } = useApi()
const {prefixCls} = useDesign('mai-wrap');
const {setTotal, current, total, setCurrentPage, limit, pagesRef} = usePagination(1, 4, 0)
const tabs = ref([
{
name: '动态资讯',
id: randomUUID(),
children: [
{name: 'WTO资讯', id: randomUUID()},
{name: '国内资讯', id: randomUUID()},
{name: '国外资讯', id: randomUUID()},
],
},
]);
const currentTab = ref({});
const content = ref([]);
const loading = ref(true);
const searchData = ref({})
async function handleChange(data:any) {
currentTab.value = data;
setCurrentPage(1)
await getList(data)
}
async function getList(data:any) {
if(data) {
loading.value = true;
const {data:listDatas} = await warningApi.getWarningList({...searchData.value,page: current.value, limit, category_id: data.id})
content.value = listDatas.data;
loading.value = false;
setTotal(listDatas.total)
}
}
function onChangePage(val: number) {
setCurrentPage(val)
getList(currentTab.value)
}
async function getData() {
const cate = await warningApi.getWarningCate()
tabs.value[0].children = cate.data
await handleChange(cate.data[0]);
}
getData()
</script>
<style scoped lang="less">
.dynamicInfo {
margin-top: 0;
padding-bottom: 100px;
}
</style>

@ -0,0 +1,4 @@
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="128" height="128" fill="none"/>
<path d="M15.36 7.6L8.832 14C18.048 20.4 25.344 26.544 30.72 32.304L36.992 26.032C31.104 19.888 23.808 13.744 15.36 7.6ZM11.904 38.192L5.376 44.464C14.592 51.12 21.888 57.392 27.008 63.28L33.28 57.008C27.52 50.608 20.352 44.336 11.904 38.192ZM25.728 73.264C20.608 88.752 14.464 103.856 7.424 118.576L16.128 122.416C22.528 108.08 28.416 92.72 33.536 76.464L25.728 73.264ZM55.168 5.296V16.304H40.32V24.624H55.168V69.296H37.632V77.872H46.464V119.6H117.504V111.024H55.296V77.872H122.88V69.296H106.368V24.624H120.064V16.304H106.368V5.296H97.408V16.304H64.128V5.296H55.168ZM97.408 24.624V34.608H64.128V24.624H97.408ZM64.128 69.296V59.056H97.408V69.296H64.128ZM64.128 51.76V42.032H97.408V51.76H64.128ZM75.904 82.736C71.552 90.16 66.432 96.56 60.544 101.936L67.456 106.928C73.344 101.04 78.592 94.128 82.944 86.192L75.904 82.736ZM97.664 82.224L90.88 86.448C98.176 93.872 103.936 100.784 108.16 106.928L115.456 101.808C111.36 96.304 105.344 89.776 97.664 82.224Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

@ -0,0 +1,86 @@
// import type { ProjectConfig } from '/#/config';
// import { MenuTypeEnum, MenuModeEnum, TriggerEnum, MixSidebarTriggerEnum } from '@/enums/menuEnum';
// import { CacheTypeEnum } from '@/enums/cacheEnum';
// import {
// ContentEnum,
// PermissionModeEnum,
// ThemeEnum,
// RouterTransitionEnum,
// SettingButtonPositionEnum,
// SessionTimeoutProcessingEnum,
// } from '@/enums/appEnum';
// import { SIDE_BAR_BG_COLOR_LIST, HEADER_PRESET_BG_COLOR_LIST } from './designSetting';
// import { primaryColor } from '../../build/config/themeConfig';
// ! You need to clear the browser cache after the change
const setting = {
showSettingButton: true,
showDarkModeToggle: true,
settingButtonPosition: 'auto',
permissionMode: 'ROUTE_MAPPING',
permissionCacheType: 1,
sessionTimeoutProcessing: 0,
themeColor: '#0960bd',
grayMode: false,
colorWeak: false,
fullContent: false,
contentMode: 'full',
showLogo: true,
showFooter: true,
headerSetting: {
bgColor: '#ffffff',
fixed: false,
show: true,
theme: 'light',
useLockPage: true,
showFullScreen: true,
showDoc: true,
showNotice: true,
showSearch: false,
},
menuSetting: {
bgColor: '#001529',
fixed: false,
collapsed: false,
siderHidden: false,
collapsedShowTitle: false,
canDrag: false,
show: true,
hidden: false,
menuWidth: 210,
mode: 'horizontal',
type: 'top-menu',
theme: 'dark',
topMenuAlign: 'end',
trigger: 'HEADER',
accordion: false,
closeMixSidebarOnChange: false,
mixSideTrigger: 'click',
mixSideFixed: false,
split: false,
},
multiTabsSetting: {
cache: false,
show: false,
canDrag: true,
showQuick: true,
showRedo: true,
showFold: true,
},
transitionSetting: {
enable: true,
basicTransition: 'fade-slide',
openPageLoading: true,
openNProgress: false,
},
openKeepAlive: true,
lockTime: 0,
showBreadCrumb: false,
showBreadCrumbIcon: false,
useErrorHandle: false,
useOpenBackTop: true,
canEmbedIFramePage: true,
closeMessageOnSwitch: true,
removeAllHttpPending: false,
};
export default setting;

@ -0,0 +1,4 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json",
}

@ -0,0 +1,57 @@
import { encrypt, decrypt } from 'crypto-js/aes';
import { parse } from 'crypto-js/enc-utf8';
import pkcs7 from 'crypto-js/pad-pkcs7';
import ECB from 'crypto-js/mode-ecb';
import md5 from 'crypto-js/md5';
import UTF8 from 'crypto-js/enc-utf8';
import Base64 from 'crypto-js/enc-base64';
export interface EncryptionParams {
key: string;
iv: string;
}
export class AesEncryption {
private key;
private readonly iv;
constructor(opt: Partial<EncryptionParams> = {}) {
const { key, iv } = opt;
if (key) {
this.key = parse(key);
}
if (iv) {
this.iv = parse(iv);
}
}
get getOptions() {
return {
mode: ECB,
padding: pkcs7,
iv: this.iv,
};
}
encryptByAES(cipherText: string) {
// @ts-ignore
return encrypt(cipherText, this.key, this.getOptions).toString();
}
decryptByAES(cipherText: string) {
// @ts-ignore
return decrypt(cipherText, this.key, this.getOptions).toString(UTF8);
}
}
export function encryptByBase64(cipherText: string) {
return UTF8.parse(cipherText).toString(Base64);
}
export function decodeByBase64(cipherText: string) {
return Base64.parse(cipherText).toString(UTF8);
}
export function encryptByMd5(password: string) {
return md5(password).toString();
}

@ -0,0 +1,17 @@
/**
* Independent time operation tool to facilitate subsequent switch to dayjs
*/
import dayjs from 'dayjs';
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
const DATE_FORMAT = 'YYYY-MM-DD';
export function formatToDateTime(date?: dayjs.ConfigType, format = DATE_TIME_FORMAT): string {
return dayjs(date).format(format);
}
export function formatToDate(date?: dayjs.ConfigType, format = DATE_FORMAT): string {
return dayjs(date).format(format);
}
export const dateUtil = dayjs;

@ -0,0 +1,91 @@
function transform(error: any) {
console.error('error', error)
const err = (text: string) => {
ElMessage.error(error?.response?._data.message ?? text);
};
if (error?.response?._data) {
switch (error.response.status) {
case 404:
err("服务器资源不存在");
break;
case 500:
err("服务器内部错误");
break;
case 401:
// 清除缓存
err("登录状态已过期,需要重新登录");
// TODO 跳转到登录界面
break;
case 403:
err("没有权限访问该资源");
break;
default:
err(error.response._data.msg);
}
} else {
err("请求超时,服务器无响应!");
}
}
/**
* api
* @param { String } url
* @param { Object } options useFtech
* @param { Object } headers header, headers
*/
const fetch = $fetch.create({
async onRequest({options}){
options.baseURL = '/api';
console.log('options>>>', options)
options.headers = new Headers(options.headers);
},
// 响应拦截
onResponse(ctx) {
const {response} = ctx;
let data = response._data;
// 在这里判断错误
if (data.code != 1) {
transform(ctx)
return Promise.reject(ctx)
}
// 成功返回
return data;
},
// 错误处理
onResponseError(error) {
transform(error)
return Promise.reject(error?.response?._data ?? null);
}
})
export default class Http<T> {
contact(...params: string[]) {
console.log('params>>>>', params)
return params.join('')
}
toFormData(data: { [x: string]: any; }) {
const dataParams = new FormData();
for (const dataKey in data) {
dataParams.append(dataKey, data[dataKey])
}
return dataParams
}
get(url: string, params?: any, headers?: any) {
console.log('url>>>>', url)
return fetch<T>(url, {method: 'get', params, headers})
}
post(url: string, params?: any, headers?: any) {
return fetch<T>(url, {method: 'post', body:params, headers})
}
put(url: string, params?: any, headers?: any){
return fetch<T>(url, {method: 'put', body:params, headers})
}
delete(url: string, params?: any, headers?: any) {
return fetch<T>(url, {method: 'delete', body:params, headers})
}
}

@ -0,0 +1,91 @@
import type { RouteLocationNormalized, RouteRecordNormalized } from 'vue-router';
import type { App, Component } from 'vue';
import { unref } from 'vue';
import { isObject } from './is';
import { cloneDeep } from 'lodash-es';
export const noop = () => {};
/**
* @description: Set ui mount node
*/
export function getPopupContainer(node?: HTMLElement): HTMLElement {
return (node?.parentNode as HTMLElement) ?? document.body;
}
/**
* Add the object as a parameter to the URL
* @param baseUrl url
* @param obj
* @returns {string}
* eg:
* let obj = {a: '3', b: '4'}
* setObjToUrlParams('www.baidu.com', obj)
* ==>www.baidu.com?a=3&b=4
*/
export function setObjToUrlParams(baseUrl: string, obj: any): string {
let parameters = '';
for (const key in obj) {
parameters += key + '=' + encodeURIComponent(obj[key]) + '&';
}
parameters = parameters.replace(/&$/, '');
return /\?$/.test(baseUrl) ? baseUrl + parameters : baseUrl.replace(/\/?$/, '?') + parameters;
}
// 深度合并
export function deepMerge<T = any>(src: any = {}, target: any = {}): T {
let key: string;
const res: any = cloneDeep(src);
for (key in target) {
res[key] = isObject(res[key]) ? deepMerge(res[key], target[key]) : (res[key] = target[key]);
}
return res;
}
export function openWindow(
url: string,
opt?: { target?: any | string; noopener?: boolean; noreferrer?: boolean },
) {
const { target = '__blank', noopener = true, noreferrer = true } = opt || {};
const feature: string[] = [];
noopener && feature.push('noopener=yes');
noreferrer && feature.push('noreferrer=yes');
window.open(url, target, feature.join(','));
}
// https://github.com/vant-ui/vant/issues/8302
type EventShim = {
new (...args: any[]): {
$props: {
onClick?: (...args: any[]) => void;
};
};
};
export type WithInstall<T> = T & {
install(app: App): void;
} & EventShim;
export type CustomComponent = Component & { displayName?: string };
export const withInstall = <T extends CustomComponent>(component: T, alias?: string) => {
(component as Record<string, unknown>).install = (app: App) => {
const compName = component.name || component.displayName;
if (!compName) return;
app.component(compName, component);
if (alias) {
app.config.globalProperties[alias] = component;
}
};
return component as WithInstall<T>;
};
export function randomUUID(): string {
function S4() {
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
}
return `${S4() + S4()}-${S4()}-${S4()}-${S4()}-${S4() + S4() + S4()}`;
}

@ -0,0 +1,98 @@
const toString = Object.prototype.toString;
export function is(val: unknown, type: string) {
return toString.call(val) === `[object ${type}]`;
}
export function isDef<T = unknown>(val?: T): val is T {
return typeof val !== 'undefined';
}
export function isUnDef<T = unknown>(val?: T): val is T {
return !isDef(val);
}
export function isObject(val: any): val is Record<any, any> {
return val !== null && is(val, 'Object');
}
export function isEmpty<T = unknown>(val: T): val is T {
if (isArray(val) || isString(val)) {
return val.length === 0;
}
if (val instanceof Map || val instanceof Set) {
return val.size === 0;
}
if (isObject(val)) {
return Object.keys(val).length === 0;
}
return false;
}
export function isDate(val: unknown): val is Date {
return is(val, 'Date');
}
export function isNull(val: unknown): val is null {
return val === null;
}
export function isNullAndUnDef(val: unknown): val is null | undefined {
return isUnDef(val) && isNull(val);
}
export function isNullOrUnDef(val: unknown): val is null | undefined {
return isUnDef(val) || isNull(val);
}
export function isNumber(val: unknown): val is number {
return is(val, 'Number');
}
export function isPromise<T = any>(val: unknown): val is Promise<T> {
return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch);
}
export function isString(val: unknown): val is string {
return is(val, 'String');
}
export function isFunction(val: unknown): val is Function {
return typeof val === 'function';
}
export function isBoolean(val: unknown): val is boolean {
return is(val, 'Boolean');
}
export function isRegExp(val: unknown): val is RegExp {
return is(val, 'RegExp');
}
export function isArray(val: any): val is Array<any> {
return val && Array.isArray(val);
}
export function isWindow(val: any): val is Window {
return typeof window !== 'undefined' && is(val, 'Window');
}
export function isElement(val: unknown): val is Element {
return isObject(val) && !!val.tagName;
}
export function isMap(val: unknown): val is Map<any, any> {
return is(val, 'Map');
}
export const isServer = typeof window === 'undefined';
export const isClient = !isServer;
export function isUrl(path: string): boolean {
const reg = /^http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/;
return reg.test(path);
}

@ -0,0 +1,9 @@
const projectName = import.meta.env.VITE_GLOB_APP_TITLE;
export function warn(message: string) {
console.warn(`[${projectName} warn]:${message}`);
}
export function error(message: string) {
throw new Error(`[${projectName} error]:${message}`);
}

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save