vue项目之html+javaScript
后端项目学习之vue
·
文章目录
整体
node
node的环境变量配置
用户变量:
系统变量:
path变量:加D:\soft\node\v22\node-v22.13.0-win-x64
NODE_PATH 值:D:\soft\node\v22\node-v22.13.0-win-x64\node_modules
js
路由配置对象
const router = [
{
path: '/xxx', // 路由路径:当浏览器地址栏为 /xxx 时匹配此路由
name: 'xxx', // 路由名称:可用于编程式导航
component: () => import('../xxx/xxx/xxx.vue'), // 指定该路由对应的组件,使用动态 import 实现懒加载(按需加载)
meta: { // 定义元信息
auth: true, // 设置访问该页面的权限,需要用户已登录(权限控制用)
title: '账单明细' // 设置页面标题(可在路由守卫中读取并 document.title = to.meta.title)
}
}
]
组件定义
export default {
name: 'xxx', // 组件的注册名称,可用于调试工具或递归组件调用。
props: {}, // 定义父组件传递给该组件的属性(props)。为空表示不接受外部传参
data() { // 组件的响应式数据
return {
isClosed: false,
statusDefault: '',
companyName: '',
typeDefault: '',
statusList: [],
typeList: [],
orderNo: '',
startTime: '',
endTime: '',
tableData: [],
pagination: {
pageSize: 10, // 每页显示的条数
currentPage: 1, // 当前页码
total: 0 // 数据总条数
},
}
},
component: {
// 用于注册局部子组件
// 当前写成 component(单数)会导致子组件无法正确注册
},
methods: {
// 定义组件内的方法(如搜索、重置、分页切换等)
}
方法导入
- 全局挂载到Vue原型
// 在main.js(项目的入口文件)中 导入;
import { isAuth } from "./utils/tools.js";
- 全局混入
// main.js
Vue.mixin({
methods: {
isAuth
}
})
- 局部导入
<script>
import { isAuth } from "@/utils/tools.js";
</script>
嵌套路由
// 父子路由
{
// 父路由
path: '/OneName', // 路由路径
name: 'OneName', // 路由名称
redirect: '/OneName/index', // 重定向:访问父路由时自动跳到 /index
component: () => import('../views/OneNameAuthentication/index'), // 父组件(路由布局容器)
children: [{
path: 'index',
name: 'index',
component: () => import('../views/OneNameAuthentication/OneName'),
meta: { title: '实名认证' } // // 元数据(页面标题)
}, {
path: 'userInfo',
name: 'userInfo',
component: () => import('../views/OneNameAuthentication/userInfo'),
meta: { title: '用户信息' }
}],
},
vue-html
vscode运行vue前端项目
node.js 版本v16.20.1
npm 版本v11.0.0
* 如果vscode执行npm install 失败,可以删掉package-lock.json文件,然后再执行npm install 就不报错了;
*
标准vue文件模板
vue2
<script>
export default {
// 组件名
name: 'StandardVueTemplate',
// 组件的输入参数,父组件传递给子组件的
props: {
title: {
type: String,
default: '默认标题'
}
},
// 注册子组件
components: {
ChildComponent
},
// 定义响应式数据,当 data 里的数据发生变化时,页面会自动更新。
data() {
return {
count: 0,
isShowContent: true
}
},
// 计算属性,即所使用到的计算方法
computed: {
doubleCount() {
return this.count * 2
}
},
// 监听数据
watch: {
count(newVal, oldVal) {
console.log(`计数从 ${oldVal} 变为 ${newVal}`)
}
},
// 定义所使用方法
methods: {
handleIncrement() {
this.count++
}
},
// 生命周期,挂载完成,当组件已经渲染到页面上(DOM 已经生成)后执行。
// 通常用于初始化数据或操作 DOM。
mounted() {
console.log('组件已挂载')
}
}
</script>
vue3
<!--
文件名:StandardVueTemplate.vue
功能描述:Vue 单文件组件(SFC)标准模板,涵盖核心开发逻辑
适用版本:Vue 3 (Composition API + <script setup>)
备注:Vue 2 选项式 API 版本在模板末尾补充
Vue 单文件组件核心分为 <template>(结构)、<script>(逻辑)、<style>(样式)三部分,各司其职且低耦合;
-->
<template>
<!-- 1. 模板区域:负责页面结构渲染,遵循 HTML 语法 + Vue 指令 -->
<!-- 根节点:Vue 3 支持多根节点,但推荐统一包裹以保持结构清晰 -->
<div class="standard-vue-container">
<!-- 1.1 数据渲染:插值表达式 -->
<h1>{{ pageTitle }}</h1>
<!-- 1.2 条件渲染:v-if/v-else -->
<div v-if="isShowContent" class="content">
<p>当前计数:{{ count }}</p>
<!-- 1.3 事件绑定:v-on (简写 @) -->
<button @click="handleIncrement">点击增加</button>
<button @click="handleReset">重置计数</button>
</div>
<div v-else>
<p>内容已隐藏</p>
<button @click="toggleContent">显示内容</button>
</div>
<!-- 1.4 列表渲染:v-for (必须绑定 key) -->
<ul class="list">
<li v-for="(item, index) in listData" :key="index">
{{ index + 1 }}. {{ item.name }} - {{ item.desc }}
</li>
</ul>
<!-- 1.5 双向绑定:v-model -->
<input v-model="inputValue" placeholder="请输入内容" />
<p>你输入的内容:{{ inputValue }}</p>
<!-- 1.6 组件通信:父传子(props)示例 -->
<ChildComponent :parentMsg="parentMessage" @childEvent="handleChildEvent" />
</div>
</template>
<script setup>
/*
2. 脚本区域:负责业务逻辑处理(Vue 3 Composition API + <script setup>)
核心逻辑:数据定义 → 方法定义 → 生命周期 → 依赖引入 → 组件通信
*/
// 2.1 依赖引入(按需导入)
import { ref, reactive, onMounted, watch, computed } from 'vue'
import ChildComponent from './ChildComponent.vue' // 引入子组件
// 2.2 组件通信:父传子(定义 props)
const props = defineProps({
title: {
type: String,
default: '默认标题'
}
})
// 2.3 组件通信:子传父(定义 emit)
const emit = defineEmits(['updateTitle'])
// 2.4 响应式数据定义
// 基础类型响应式数据(ref)
const pageTitle = ref(props.title)
const count = ref(0)
const isShowContent = ref(true)
const inputValue = ref('')
// 复杂类型响应式数据(reactive)
const listData = reactive([
{ name: 'Vue 模板', desc: '负责页面结构' },
{ name: 'Vue 脚本', desc: '负责业务逻辑' },
{ name: 'Vue 样式', desc: '负责页面样式' }
])
// 2.5 计算属性(基于现有数据派生新数据)
const doubleCount = computed(() => {
return count.value * 2
})
// 2.6 方法定义(处理业务逻辑)
const handleIncrement = () => {
count.value++
}
const handleReset = () => {
count.value = 0
}
const toggleContent = () => {
isShowContent.value = !isShowContent.value
}
const handleChildEvent = (msg) => {
pageTitle.value = msg
// 触发自定义事件,向父组件传递数据
emit('updateTitle', msg)
}
// 2.7 生命周期钩子(组件生命周期)
// 组件挂载完成后执行
onMounted(() => {
console.log('组件已挂载')
// 模拟接口请求获取数据
setTimeout(() => {
listData.push({ name: '生命周期', desc: '组件从创建到销毁的过程' })
}, 1000)
})
// 2.8 监听数据变化(watch)
watch(count, (newVal, oldVal) => {
console.log(`计数从 ${oldVal} 变为 ${newVal}`)
// 计数超过 10 时重置
if (newVal > 10) {
handleReset()
}
})
// 2.9 暴露变量/方法(供父组件调用)
defineExpose({
count,
handleReset
})
</script>
<style scoped lang="scss">
/*
3. 样式区域:负责页面样式
核心配置:
- scoped:样式仅作用于当前组件(防止样式污染)
- lang:指定预处理器(scss/less/stylus)
*/
// 3.1 基础样式
.standard-vue-container {
padding: 20px;
max-width: 800px;
margin: 0 auto;
}
// 3.2 内容区域样式
.content {
margin: 20px 0;
padding: 10px;
border: 1px solid #eee;
button {
margin-right: 10px;
padding: 5px 10px;
cursor: pointer;
}
}
// 3.3 列表样式
.list {
margin: 20px 0;
padding-left: 20px;
li {
margin: 5px 0;
color: #333;
}
}
// 3.4 响应式样式
@media (max-width: 768px) {
.standard-vue-container {
padding: 10px;
}
}
</style>
el-date-picker日期选择器组件
<div class="form-item clearfix">
<span class="form-label">结束日期</span>
<el-date-picker // Element UI 的日期选择器组件,用于选择日期
v-model="dateEnd" // Vue 的双向数据绑定,输入框的内容会实时同步到 Vue 实例的dateEnd变量中;
type="date" // 类型是日期,而不是年月/时间
clearable // 允许清空已选择的日期
placeholder="选择日期"> // 占位提示文字
</el-date-picker>
<span class="can-click search-button" @click="doSearch">查询</span>
// 点击事件绑定,点击 “查询” 按钮会触发 Vue 实例中的doSearch方法(查询数据的核心逻辑)。
</div>
el-table表格组件
<el-table
:data="tableData"
:border="true"
style="width: 100%"
>
<!-- :data="tableData" 数据绑定核心属性,tableData 是 Vue 组件 data 中定义的数组,数组里的每个对象对应表格的一行数据 -->
<!-- :border="true":布尔类型的绑定属性,设置为 true 表示显示表格的边框 -->
<el-table-column
prop="companyName"
label="xxx"
fixed
min-width="120"
>
<!-- 该列要展示 tableData 中每行数据的 companyName 属性值
表格表头
固定列属性,设置后该列会固定在表格左侧,当表格横向滚动时不会消失
列的最小宽度为 120px,保证内容不会被过度挤压,同时支持自适应拉伸 -->
</el-table-column>
<el-table-column
prop="deposit"
label="xxx"
align="right"
min-width="100"
>
<template slot-scope="scope">
<span class="can-click detail-btn" style="color: #2388FB" @click="turnTo('/deposit/'+scope.row.companyId)">{{scope.row.deposit}}</span>
<!-- Element UI 表格的作用域插槽,scope 是插槽的上下文对象,包含当前行的所有数据:
scope.row:表示当前行的数据对象
渲染当前行的 deposit 字段值 -->
</template>
</el-table-column>
el-select下拉选择组件
<el-select v-model="typeDefault">
<el-option
v-for="item in typeList"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
<!-- Element UI/Plus 提供的下拉选择组件。
v-model:双向绑定,将用户选择的值自动同步到 Vue 实例中的 typeDefault 数据属性;同时,typeDefault 的初始值也会决定默认选中项。
<el-option>:表示下拉菜单中的每一个选项。
v-for="item in typeList":遍历 typeList 数组(通常是一个对象数组),为每个 item 渲染一个选项。
:key="item.id":Vue 的 key 属性
:label="item.name":显示在下拉列表中的文字内容(用户看到的名称)。
:value="item.id":该选项被选中时,实际赋给 v-model的值。
-->
el-pagination分页组件
<el-pagination
layout="prev, pager, next,sizes, jumper, total"
background
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
:page-sizes="[10,15, 30, 50, 100]"
:current-page="pagination.currentPage"
:page-size="pagination.pageSize"
:total="pagination.total">
</el-pagination>
<!--
设置分页组件的布局顺序:
• prev:上一页按钮
• pager:页码列表
• next:下一页按钮
• sizes:每页显示条数选择器
• jumper:跳转到指定页输入框
• total:显示总条数(如“共 100 条”)
background:为页码按钮添加背景色(视觉更清晰)
当前页码改变时触发的事件,回调函数接收新页码(currentPage)作为参数
每页条数(pageSize)改变时触发的事件,回调函数接收新条数作为参数
定义“每页显示条数”下拉选项,用户可从中选择
绑定当前页码(通常来自 data 中的 pagination.currentPage)
绑定每页显示的条数(如 10、30 等)
绑定数据总条数,用于计算总页数和显示“共 X 条”
-->
渲染子组件
<Adjusting
v-if="showAdjusting"
:billId="billId"
:beforeMoney="beforeMoney"
@unDo="unDo"
@toDoIt="unDo"
></Adjusting>
<!--
条件渲染,只有当父组件中的数据属性 showAdjusting 为 true 时,才渲染adjusting组件
父向子传递属性,传递billId
父向子传递属性,传递beforeMoney
监听子组件触发的自定义事件unDo,当子组件通过 $emit('unDo') 触发 unDo 事件时,父组件会执行自己的 unDo 方法;
监听子组件触发的自定义事件toDoIt,当子组件通过 $emit('toDoIt') 触发 toDoIt 事件时,父组件会执行自己的 unDo 方法;
-->
ECharts 饼图
getEcharts(id) {
let dom = this.$echarts.init(document.getElementById(id));
// 使用 this.$echarts在页面中 ID 为 id 的 DOM 元素上初始化一个 ECharts 图表实例。
let seriesData = []; // 存放饼图每一块的数据(value + name)
let dataList = []; // 存放图例名称(serviceName)
let colorList = ['#c0c5e2','#eaeaea']; // 饼图颜色配置(仅两种颜色)
let getDataList = []; // 存放原始数据项(用于后续格式化图例)
//资费包
this.dataOfPackageEcharts.map((value) => {
if (value.packageMoney > 0) {
seriesData.push({
value: value.packageMoney,
name: value.serviceName,
});
// 遍历dataOfPackageEcharts数据,构建seriesData数据
dataList.push(value.serviceName); // 图例存放
getDataList.push(value); // 存放原始数据
}
});
//网易专属
let wy_this = this;
let optionOfFirst = {
tooltip: {
trigger: "item",
formatter: "{a} <br/>{b} : {c}",
},
// 提示框 鼠标悬停时显示提示:系列名(a)、数据项名(b)、数值(c)。
legend: {
type: "scroll",
orient: "vertical",
left: "65%",
top: "15%",
data: dataList,
formatter: function (name) {
getDataList.map((value) => {
if (value.packageMoney) {
if (name === value.serviceName) {
if (wy_this.isWy) {
name =
value.serviceName.split("-")[0] +
"-资源包" +
" | ¥" +
wy_this.turnToThousands(value.packageMoney);
} else {
name =
name +
" | " +
wy_this.turnToThousands(value.packageMoney);
}
}
}
});
return name;
},
},
// legend 图例
// formatter 图例名字
series: [
{
name: "账单详情统计图",
type: "pie",
radius: "55%",
center: ["30%", "50%"],
data: seriesData,
label: {
position: "outside",
formatter: "{b}:({d}%)",
},
itemStyle: {
emphasis: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)",
},
},
},
],
color: colorList,
};
// series 饼图样式设置
dom.setOption(optionOfFirst); // 将配置应用到图表。
if (colorList.length === 0) {
this.showEcharts = false; // 颜色不存在,不显示该图表
}
}
el-dropdown下拉菜单
<el-dropdown> <!-- 下拉菜单组件 -->
<span style="color:#0598fa; line-height: 30px;cursor: pointer">
操作<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<!-- 蓝色文字(Element 主题色风格)。垂直居中对齐(常用于表格行高约 30px 的场景)。鼠标悬停时显示手型光标,提示可点击。
使用 Element 的内置图标,显示一个向右对齐的小箭头(实际是向下箭头,el-icon--right 控制位置)。 -->
<el-dropdown-menu slot="dropdown">
<!-- slot="dropdown" 表示这部分内容将作为下拉面板插入到 <el-dropdown> 中-->
<el-dropdown-item @click.native="handleAuth(scope.row)">修改角色</el-dropdown-item>
<el-dropdown-item @click.native="operateDeveloper(scope.row)">省份发展人</el-dropdown-item>
<!-- -->
</el-dropdown-menu>
</el-dropdown>
el-tree树形组件
<div class="pop-header" @mousedown="mouseDownChange">
发展人省份地市选择
</div>
<!-- @mousedown="mouseDownChange":绑定鼠标按下事件,通常用于实现弹窗拖拽功能(在 mouseDownChange 方法中会记录鼠标位置并监听移动,从而拖动整个弹窗)。 -->
<div class="pop-content">
<div class="clearfix input-container">
<label>省份地市</label>
<div class="tree-container">
<el-tree
ref="tree"
:data="data"
default-expand-all
:default-checked-keys="checkedCities"
:props="defaultProps"
show-checkbox
node-key="id"
>
</el-tree>
<!-- 为该组件注册引用,可在 JS 中通过 this.$refs.tree 获取实例,用于获取选中节点等操作
data 树的数据源
默认展开所有节点(即所有省、市都展开,无需手动点击三角形展开)
初始化时默认勾选的节点 ID 列表(如 [2, 5, 8]),用于回显已选中的地市
配置节点数据的字段映射
显示复选框,支持多选
每个节点的唯一标识字段名 -->
</div>
</div>
</div>
动态class绑定
<div :class="{content_proup:true,hidden:!isShowLeftMenu}" v-show="isShowProup">
<!-- :class 的class属性绑定,
content_group 属性一直存在
hidden属性,取决于isShowLeftMenu的值;为false添加,为true不添加
v-show 该元素隐藏/显示,为true显示,为false隐藏 -->
分组权限复选框
<el-checkbox-group v-model="checked">
<div v-for="(item,index) in checkList" :key="index">
<div class="sort_name">{{item[0].permissionGroup}}</div>
<div class="sort_content">
<el-checkbox :label="value.id" v-for="(value,key) in item" :key="key">{{value.permissionNameZh}}</el-checkbox>
</div>
</div>
</el-checkbox-group>
<!--复选框组组件
v-for 遍历
每个 item 代表一个权限组(例如“用户管理”、“订单管理”等)。index 是当前组的索引(用作 key)。
-->
文件下载
<p v-if="type===2">
请先点击
<a download="发展人批量新增文件.xlsx" :href="baseUrl+'/file/发展人信息模板批量导入文件.xlsx'">下载模板</a>
,文件大小在1M以内
</p>
<!--
用于在页面上根据条件动态渲染一个带下载链接的提示段落。
HTML5 的 download 属性:告诉浏览器这个链接是用于下载文件,且下载后保存的文件名为 发展人批量新增文件.xlsx
Vue 的动态绑定语法(:href 是 v-bind:href 的简写):
• baseUrl 是 Vue 实例中的数据(比如 "https://api.example.com")
• 拼接后形成完整下载链接,如 "https://api.example.com/file/发展人信息模板批量导入文件.xlsx"
-->
文件上传
<template>
<div class="clearfix input-container">
<label class="batchImport-label">选择文件</label>
<div v-if="filename" class="filenameContainer">{{filename}}</div>
<div v-else class="filenameContainer filenameTxt">请选择文件</div>
<label class="labelForUpload" for="branchUpload">浏览</label>
<input type="file" accept="application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" style="display: none" id="branchUpload" ref="excelFile" @change="branchUpload">
<!--
type=原生文件选择输入框
accept=文件类型限制:application/vnd.ms-excel → .xls(Excel 97-2003);application/vnd.openxmlformats... → .xlsx(Excel 2007+)
style= display:none = 隐藏原生 input
ref= Vue 的模板引用,组件 JS 中可通过 this.$refs.excelFile 直接访问这个 DOM 元素(比如手动清空文件)
@change= 用户选完文件后,自动触发 branchUpload 方法处理逻辑(如读取文件名、校验大小、上传等)
-->
</div>
</template>
<!--
实现了一个自定义样式的 Excel 文件上传控件。
template = Vue 单文件组件的模板根标签
div class : CSS 类名,clearfix 用于清除浮动,input-container 是自定义布局样式
label : 静态文本标签,提示用户这里要"选择文件",纯展示用
v-if 条件渲染:如果 Vue 实例中的 filename 变量有值(用户已选文件),显示这个 div 且利用插值表达式:动态显示文件名 {{filename}}、
v-else 如果 filename 为空(未选文件),显示"请选择文件"的占位文本 class=filenameText,额外的 CSS 类,用于设置占位文本的灰色样式
for=branchUpload,关键技巧:HTML 的 label[for] 属性绑定到 id="branchUpload" 的 input 元素→ 点击这个 label,浏览器会自动触发对应 input 的点击事件,实现"假按钮控制真输入框"
-->
<script>
branchUpload(){
//excelFile
let file = this.$refs.excelFile.files[0];
let _this = this;
let reads = new FileReader();
if(file){
// 以文本方式读取文件
reads.readAsText(file,'utf-8');
reads.onabort = function (e) {
//①处理abort事件。该事件在读取操作被 中断 时触发。
_this.fileAbort(e,file,this);
};
reads.onerror = function (e) {
//②处理error事件。该事件在读取操作发生 错误 时触发。
_this.fileError(e,file,this);
};
reads.onload = function (e) {
//③处理load事件。该事件在读取操作 完成 时触发。
_this.fileLoad(e,file,this);
};
reads.onloadstart = function (e) {
//④处理loadstart事件。该事件在读取操作 开始 时触发。
_this.fileLoadStart(e,file,this);
};
reads.onloadend = function (e) {
//⑤处理loadend事件。该事件在读取操作 结束 时(结果:成功或者失败)触发。
_this.fileLoadEnd(e,file,this);
};
reads.onprogress = function (e) {
//⑥处理progress事件。该事件在 读取Blob 时触发。
_this.fileProgress(e,file,this);
};
}
}
</script>
el-dialog弹窗
<el-dialog title="是否自动发送账单" :visible.sync="autoBillPop" width="30%">
<!--
el-dialog 弹窗容器
:visible.sync="autoBillPop" --》 控制弹窗显示与隐藏的核心属性,
.sync 修饰符表示双向绑定。弹窗关闭 autoBillPop自动赋值为false,打开赋值为true
-->
<el-form>
<el-form-item label="自动发送账单:">
<el-select v-model="autoStatus" placeholder="请选择" @change="initModifyCycle">
<!--
placeholder="请选择": 未选择时显示的提示文字
-->
<el-option label="是" :value="0"></el-option>
<el-option label="否" :value="1"></el-option>
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="autoBillPop = false">取 消</el-button>
<el-button type="primary" @click="setAutoSendBill">确 定</el-button>
</div>
</el-dialog>
el-radio-group
<el-radio-group>
// 单选按钮组容器
<ul>
<li v-for="(item, index) in packageList" :key="index" @click="checkPackage(index, item)"
:class="active === index ? 'active' : 'no-active'">
<div class="quantity">
{{ item.times }}次
</div>
<div class="per-price">每条<!----><span>请求</span>
{{ (item.basicPrice / item.times).toFixed(3) }} 元</div>
<div class="order-split-line"></div>
<div class="order-price">
{{ toThousands(item.basicPrice, 2) }} 元
<div class="check-package" v-show="active === index">
<i class="el-icon-check" style="color: white"></i>
</div>
</div>
</li>
</ul>
</el-radio-group>
el-tab-pane
Vue Router 路由守卫
Vue.js 项目中使用的全局前置路由守卫,主要用于实现登录状态校验和实名认证拦截功能。
import router from './router'
const whiteList = ['smPackageList','home','product','guide','industrycase','login','register','personalRealName','serviceTerms','policyProtection','smsSpecification','forgetPassword']
// 定义了一个无需登录即可访问的页面路由名称列表
router.beforeEach(async(to, from, next) => { // 全局前置守卫 beforeEach
document.title = '创新平台统一门户-' + (to.meta.title || '') // 动态设置浏览器标签页标题
const hasToken = window.$cookies.get('Authorization')
const companyStatus = window.$cookies.get('companyStatus')
// 从 Cookie 中读取 Authorization(登录凭证)和 companyStatus(企业认证状态)
if (hasToken) {
// 如果已是登录状态
if ([-1,0,2,3].indexOf(parseInt(companyStatus)) !== -1 && to.path.indexOf('personalRealName') === -1 && to.path !== '/login'){
// 未实名
next('/personalRealName')
}else{
next()
}
} else {
/* has no token*/
if (whiteList.indexOf(to.name) !== -1) {
next()
} else {
next('/login')
}
}
})
router.afterEach(() => {
// 全局后置守卫 afterEach
})
最后
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)