Vue3项目练习详细步骤(第四部分:文章管理页面模块)

news2025/2/24 11:04:36

文章列表查询

页面主体结构 

 接口文档

 接口调用

 添加文章列表

添加组件

富文本编辑器

 封面图片上传

 接口文档

 接口调用

编辑文章列表

结构调整 

数据回显 

接口文档 

绑定请求数据

删除文章列表

接口文档 

 绑定请求数据 

文章列表查询

页面主体结构 

在ArticleManage.vue 文件中编写文章列表页面组件

<script setup>
import {
    Edit,
    Delete
} from '@element-plus/icons-vue'

import { ref } from 'vue'

//文章分类数据模型
const categorys = ref([
    {
        "id": 3,
        "categoryName": "美食",
        "categoryAlias": "my",
        "createTime": "2023-09-02 12:06:59",
        "updateTime": "2023-09-02 12:06:59"
    },
    {
        "id": 4,
        "categoryName": "娱乐",
        "categoryAlias": "yl",
        "createTime": "2023-09-02 12:08:16",
        "updateTime": "2023-09-02 12:08:16"
    },
    {
        "id": 5,
        "categoryName": "军事",
        "categoryAlias": "js",
        "createTime": "2023-09-02 12:08:33",
        "updateTime": "2023-09-02 12:08:33"
    }
])

//用户搜索时选中的分类id
const categoryId=ref('')

//用户搜索时选中的发布状态
const state=ref('')

//文章列表数据模型
const articles = ref([
    {
        "id": 5,
        "title": "陕西旅游攻略",
        "content": "兵马俑,华清池,法门寺,华山...爱去哪去哪...",
        "coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png",
        "state": "草稿",
        "categoryId": 2,
        "createTime": "2023-09-03 11:55:30",
        "updateTime": "2023-09-03 11:55:30"
    },
    {
        "id": 5,
        "title": "陕西旅游攻略",
        "content": "兵马俑,华清池,法门寺,华山...爱去哪去哪...",
        "coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png",
        "state": "草稿",
        "categoryId": 2,
        "createTime": "2023-09-03 11:55:30",
        "updateTime": "2023-09-03 11:55:30"
    },
    {
        "id": 5,
        "title": "陕西旅游攻略",
        "content": "兵马俑,华清池,法门寺,华山...爱去哪去哪...",
        "coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png",
        "state": "草稿",
        "categoryId": 2,
        "createTime": "2023-09-03 11:55:30",
        "updateTime": "2023-09-03 11:55:30"
    },
])

//分页条数据模型
const pageNum = ref(1)//当前页
const total = ref(20)//总条数
const pageSize = ref(3)//每页条数

//当每页条数发生了变化,调用此函数
const onSizeChange = (size) => {
    pageSize.value = size
}
//当前页码发生变化,调用此函数
const onCurrentChange = (num) => {
    pageNum.value = num
}
</script>
<template>
    <el-card class="page-container">
        <template #header>
            <div class="header">
                <span>文章管理</span>
                <div class="extra">
                    <el-button type="primary">添加文章</el-button>
                </div>
            </div>
        </template>
        <!-- 搜索表单 -->
        <el-form inline>
            <el-form-item label="文章分类:">
                <el-select placeholder="请选择" v-model="categoryId">
                    <el-option 
                        v-for="c in categorys" 
                        :key="c.id" 
                        :label="c.categoryName"
                        :value="c.id">
                    </el-option>
                </el-select>
            </el-form-item>

            <el-form-item label="发布状态:">
                <el-select placeholder="请选择" v-model="state">
                    <el-option label="已发布" value="已发布"></el-option>
                    <el-option label="草稿" value="草稿"></el-option>
                </el-select>
            </el-form-item>
            <el-form-item>
                <el-button type="primary">搜索</el-button>
                <el-button>重置</el-button>
            </el-form-item>
        </el-form>
        <!-- 文章列表 -->
        <el-table :data="articles" style="width: 100%">
            <el-table-column label="文章标题" width="400" prop="title"></el-table-column>
            <el-table-column label="分类" prop="categoryId"></el-table-column>
            <el-table-column label="发表时间" prop="createTime"> </el-table-column>
            <el-table-column label="状态" prop="state"></el-table-column>
            <el-table-column label="操作" width="100">
                <template #default="{ row }">
                    <el-button :icon="Edit" circle plain type="primary"></el-button>
                    <el-button :icon="Delete" circle plain type="danger"></el-button>
                </template>
            </el-table-column>
            <template #empty>
                <el-empty description="没有数据" />
            </template>
        </el-table>
        <!-- 分页条 -->
        <el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[3, 5 ,10, 15]"
            layout="jumper, total, sizes, prev, pager, next" background :total="total" @size-change="onSizeChange"
            @current-change="onCurrentChange" style="margin-top: 20px; justify-content: flex-end" />
    </el-card>
</template>
<style lang="scss" scoped>
.page-container {
    min-height: 100%;
    box-sizing: border-box;

    .header {
        display: flex;
        align-items: center;
        justify-content: space-between;
    }
}
</style>

 文章列表数据回显

//文章列表查询
import { articleCategoryListService } from '@/api/article.js'
const getArticleCategoryList = async () => {
    //获取所有分类
    let resultC = await articleCategoryListService();
    categorys.value = resultC.data
}
getArticleCategoryList();

 接口文档

 接口调用

在article.js文件中提供获取文章列表数据的函数  

//文章列表查询
export const articleListService = (params) => {
    return request.get('/article', { params: params })
}

 在ArticleManage.vue文件中,定义调用接口获取数据的函数

//文章列表查询
import { articleListService } from '@/api/article.js'
const getArticles = async () => {
    let params = {
        pageNum: pageNum.value,
        pageSize: pageSize.value,
        categoryId: categoryId.value ? categoryId.value : null,
        state: state.value ? state.value : null
    }
    let result = await articleListService(params);
    //渲染列表数据
    articles.value = result.data.items
    //渲染总条数
    total.value=result.data.total
    //为列表中添加categoryName属性
    for(let i=0;i<articles.value.length;i++){
        let article = articles.value[i];
        for(let j=0;j<categorys.value.length;j++){
            if(article.categoryId===categorys.value[j].id){
                article.categoryName=categorys.value[j].categoryName
            }
        }
    }

}
getArticles();

在分类列绑定为categoryName

<el-table-column label="分类" prop="categoryName"></el-table-column>

 保存查看效果

 为搜索按钮绑定单击事件,调用getArticles函数即可

<el-button type="primary" @click="getArticles()">搜索</el-button>

 为重置按钮绑定单击事件,清除categoryId和state的之即可

<el-button @click="categoryId='';state='';getArticles()">重置</el-button>

当分页条的当前页和每页条数发生变化,重新发送请求获取数据

//当每页条数发生了变化,调用此函数
const onSizeChange = (size) => {
    pageSize.value = size
    getArticles()

}
//当前页码发生变化,调用此函数
const onCurrentChange = (num) => {
    pageNum.value = num
    getArticles()
}

 添加文章列表

 添加组件

 在ArticleManage.vue文件中添加抽屉组件

import {Plus} from '@element-plus/icons-vue'
//控制抽屉是否显示
const visibleDrawer = ref(false)
//添加表单数据模型
const articleModel = ref({
    title: '',
    categoryId: '',
    coverImg: '',
    content:'',
    state:''
})

<!-- 抽屉 -->
        <el-drawer v-model="visibleDrawer" title="添加文章" direction="rtl" size="50%">
            <!-- 添加文章表单 -->
            <el-form :model="articleModel" label-width="100px" >
                <el-form-item label="文章标题" >
                    <el-input v-model="articleModel.title" placeholder="请输入标题"></el-input>
                </el-form-item>
                <el-form-item label="文章分类">
                    <el-select placeholder="请选择" v-model="articleModel.categoryId">
                        <el-option v-for="c in categorys" :key="c.id" :label="c.categoryName" :value="c.id">
                        </el-option>
                    </el-select>
                </el-form-item>
                <el-form-item label="文章封面">

                    <el-upload class="avatar-uploader" :auto-upload="false" :show-file-list="false">
                        <img v-if="articleModel.coverImg" :src="articleModel.coverImg" class="avatar" />
                        <el-icon v-else class="avatar-uploader-icon">
                            <Plus />
                        </el-icon>
                    </el-upload>
                </el-form-item>
                <el-form-item label="文章内容">
                    <div class="editor">富文本编辑器</div>
                </el-form-item>
                <el-form-item>
                    <el-button type="primary">发布</el-button>
                    <el-button type="info">草稿</el-button>
                </el-form-item>
            </el-form>
        </el-drawer>

  在ArticleManage.vue文件中抽屉组件样式

/* 抽屉样式 */
.avatar-uploader {
    :deep() {
        .avatar {
            width: 178px;
            height: 178px;
            display: block;
        }

        .el-upload {
            border: 1px dashed var(--el-border-color);
            border-radius: 6px;
            cursor: pointer;
            position: relative;
            overflow: hidden;
            transition: var(--el-transition-duration-fast);
        }

        .el-upload:hover {
            border-color: var(--el-color-primary);
        }

        .el-icon.avatar-uploader-icon {
            font-size: 28px;
            color: #8c939d;
            width: 178px;
            height: 178px;
            text-align: center;
        }
    }
}
.editor {
  width: 100%;
  :deep(.ql-editor) {
    min-height: 200px;
  }
}

 在添加文章按钮处绑定抽屉显示事件

<el-button type="primary" @click="visibleDrawer = true">添加文章</el-button>

富文本编辑器

文章内容需要使用到富文本编辑器,这里咱们使用一个开源的富文本编辑器 Quill

官网: https://vueup.github.io/vue-quill/

在项目目录下安装富文本编辑器

npm install @vueup/vue-quill@latest --save

 导入组件和样式

import { QuillEditor } from '@vueup/vue-quill'
import '@vueup/vue-quill/dist/vue-quill.snow.css'

 在页面中使用quill组件

保存后查看效果

 封面图片上传

        将来当点击+图标,选择本地图片后,el-upload这个组件会自动发送请求,把图片上传到指定的服务器上,而不需要我们自己使用axios发送异步请求,所以需要给el-upload标签添加一些属性,控制请求的发送  

  • auto-upload:是否自动上传
  • action: 服务器接口路径
  • name: 上传的文件字段名
  • headers: 设置上传的请求头
  • on-success: 上传成功的回调函数

文件上传接口文档

导入token

//导入token
import { useTokenStore } from '@/stores/token.js'
const tokenStore = useTokenStore();

定义上传成功后的回调函数

//上传图片成功回调
const uploadSuccess = (img) => {
    //img就是后台响应的数据,格式为:{code:状态码,message:提示信息,data: 图片的存储地址}
    articleModel.value.coverImg=img.data
    console.log(img.data);
}

 设置el-upload标签属性

<el-upload class="avatar-uploader" :auto-upload="true" :show-file-list="false"
action="/api/upload" name = 'file' :headers="{'Authorization':tokenStore.token}" :on-success="uploadSuccess">

练习项目后端使用的是本地文件存储,所以没有用到网络url,传入本地文件地址后端会报如下错误

点击上传由于这里练习项目没有网络服务器 数据是上传到本地的所以浏览器会拦截本地文件加载导致不能造成数据回显,但是目录下是有图片成功上传到的。

后续继续完成项目建议修改src中的值为模拟的固定网络图片url地址:R-C.4bdc8f7f0e0201905fe400fb5156b7c7 (592×362) (bing.net)

<img v-if="articleModel.coverImg" :src="'https://ts1.cn.mm.bing.net/th/id/R-C.4bdc8f7f0e0201905fe400fb5156b7c7?rik=MVFo1SU7cYgFqg&riu=http%3a%2f%2fwww.spasvo.com%2fckfinder%2fuserfiles%2fimages%2f2020061536450116.jpg&ehk=r7Pp%2fX3wIOhP%2fcuW0ITLAHeD0sZPNatsyfpC3XWOM0s%3d&risl=&pid=ImgRaw&r=0'" class="avatar" />

 如果自己有网络服务器可以使用网络服务器的图片url

 接口文档

 

 接口调用

 在article.js文件中提供添加文章列表数据的函数  

//添加文章
export const articleAddService = (articleModel)=>{
    return request.post('/article',articleModel)
}

 在ArticleManage.vue中提供addArticle函数完成添加文章接口的调用

//导入Element-Plus提示框组件
import { ElMessage } from 'element-plus'
//导入articleAddService函数
import {articleAddService} from '@/api/article.js'
//添加文章
const addArticle=async (state)=>{
    articleModel.value.state = state
    //由于本地文件存储 这里测试使用固定的网络url 如果有网络服务器存储那就可以不需要这个
    articleModel.value.coverImg = 'https://ts1.cn.mm.bing.net/th/id/R-C.4bdc8f7f0e0201905fe400fb5156b7c7?rik=MVFo1SU7cYgFqg&riu=http%3a%2f%2fwww.spasvo.com%2fckfinder%2fuserfiles%2fimages%2f2020061536450116.jpg&ehk=r7Pp%2fX3wIOhP%2fcuW0ITLAHeD0sZPNatsyfpC3XWOM0s%3d&risl=&pid=ImgRaw&r=0'
    let result = await articleAddService(articleModel.value);
    if(result.code == 0) {
        //成功
        ElMessage.success(result.message? result.message:'添加成功')
        //再次调用getArticles,获取文章
        getArticles()
        //隐藏抽屉
        visibleDrawer.value=false
    }else{
        //失败
        ElMessage.error('添加失败')
    }
}

 给已发布和草稿按钮绑定事件

<el-button type="primary" @click="addArticle('已发布')">发布</el-button>
<el-button type="info" @click="addArticle('草稿')">草稿</el-button>

保存后发布文章查看已成功发布

编辑文章列表

结构调整 

  在编辑按钮处添加事件,点击后显示弹框

//定义变量控制弹窗标题
const titles=ref('')

在弹窗标题中绑定变量

<el-drawer v-model="visibleDrawer" :title="titles" direction="rtl" size="50%">

  在添加文章按钮中赋值给标题变量

<el-button type="primary" @click="visibleDrawer = true;titles='添加文章'" >添加文章</el-button>

 在编辑按钮中赋值标题变量

<el-button :icon="Edit" circle plain type="primary" @click="visibleDrawer = true;titles='编辑文章'"></el-button>

数据回显 

定义数据回显函数

//编辑文章回显
const updateCategoryEcho = (row) => {
    titles.value = '编辑文章'
    visibleDrawer.value = true
    //将row中的数据赋值给categoryModel
    articleModel.value.title = row.title
    articleModel.value.categoryId = row.categoryId
    articleModel.value.coverImg = row.coverImg
    articleModel.value.content = row.content
    articleModel.value.state = row.state
    //修改的时候必须传递分类的id,所以扩展一个id属性
    articleModel.value.id=row.id
}

通过插槽的方式得到被点击按钮所在行的数据

<el-button :icon="Edit" circle plain type="primary" @click="updateCategoryEcho(row)"></el-button>

保存后点击编辑 文本框回显效果

接口文档 

 绑定请求数据

 在article.js文件中添加请求函数

//修改文章
export const articleManageUpdateService = (articleModel) => {
    return request.put('/article',articleModel)
}

 在文章列表页面ArticleManage.vue文件中添加修改文章单击事件请求函数 

//导入articleManageUpdateService函数
import {articleManageUpdateService} from '@/api/article.js'
//编辑文章
const updateManage = async () => {
    let result = await articleManageUpdateService(articleModel.value)
    if(result.code === 0){
        //成功
        ElMessage.success(result.message ? result.message:'编辑成功')
        //隐藏弹窗
        visibleDrawer.value = false
        //刷新分类列表 再次调用getArticles,获取文章
        getArticles()
    }else{
        //失败
        ElMessage.error('编辑失败')
    }
}

由于现在修改和新增共用了一个数据模型,所以在点击添加分类后,有时候会显示数据,此时可以将categoryModel中的数据清空 

//清空模型数据
const clearData = () => {
    visibleDrawer.value = ''
    articleModel.value.title = ''
    articleModel.value.categoryId = ''
    articleModel.value.coverImg = ''
    articleModel.value.content = ''
    articleModel.value.state = ''
}

 给添加文章按钮的绑定清空数据回显单击事件 

<el-button type="primary" @click="visibleDrawer = true;titles='添加文章';clearData()" >添加文章</el-button>

  修改发布和草稿按钮的绑定事件

<el-button type="primary" @click="titles === '添加文章' ? addArticle('已发布'):updateManage('已发布')" >发布</el-button>
<el-button type="info" @click="titles === '编辑文章' ? addArticle('草稿'):updateManage('草稿')">草稿</el-button>

 保存之后就能完成添加和修改的功能了 

删除文章列表

接口文档 

 绑定请求数据 

在article.js文件中添加请求函数

//删除文章
export const articleManageDeleteService = (id) => {
    return request.delete('/article?id='+id)
}

 在文章分类页面ArticleManage.vue文件中编写删除文章单击事件请求函数并在函数内部添加提示框组件

//导入element的ElMessageBox提示框组件
import { ElMessageBox } from 'element-plus'
//导入articleManageDeleteService函数
import {articleManageDeleteService} from '@/api/article.js'
//删除分类
const deleteManage = (row) => {
    ElMessageBox.confirm(
        '确认是否删除该分类信息?',
        '提示',
        {
            confirmButtonText: '确认',
            cancelButtonText: '取消',
            type: 'warning',
        }
    )
        .then(async () => {
            //用户点击了确认
            let result = await articleManageDeleteService(row.id)
            ElMessage.success(result.message?result.message:'删除成功')
            //再次调用getAllCategory,获取所有文章分类
            getArticles()
        })
        .catch(() => {
            //用户点击了取消
            ElMessage({
                type: 'info',
                message: '取消删除',
            })
        })
}

 在删除图标按钮处绑定该点击事件函数

<el-button :icon="Delete" circle plain type="danger" @click="deleteManage(row)"></el-button>

保存后即可正常删除文章

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1719938.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

太速科技-基于3U VPX 4核8线程I7 X86主板

基于3U VPX 4核8线程I7 X86主板 一、产品概述 该产品是一款基于第六代Intel i7四核八线程处理器的高性能3U VPX刀片式计算机。产品提供了4个x4 PCIe 3.0总线接口&#xff0c;其中2个x4 PCIe 3.0接口可配置为1个x8 PCIe3.0接口&#xff0c;另外2个x4 PCIe 3.0接口可灵活配置…

微信小程序教程DAY3

box标签 第二种方法 绿色第一种 第一种更好 效果一样 完成这个项目 先写循环

Git基本使用教程(学习记录)

参考文章链接&#xff1a; Git教程&#xff08;超详细&#xff0c;一文秒懂&#xff09; RUNOOB Git教程 Git学习记录 1Git概述 1.1版本控制软件功能 版本管理&#xff1a;更新或回退到历史上任何版本&#xff0c;数据备份共享代码&#xff1a;团队间共享代码&#xff0c;…

mirth Connect 自定义JAVA_HOME

mirth Connect 自定义JAVA_HOME 1、背景 服务器上安装了两个不同版本的Java&#xff0c;我希望Mirth服务使用与默认系统不同的版本。自定义指定java版本 2、解决方法 2.1 优先级说明 系统变量JAVA_HOME (设置后&#xff0c;mirth会根据这个进行启动运行服务&#xff0c;优先级…

2020 6.s081——Lab2:system calls

左岸的一座白色环形阶梯 浪人正在用和弦练习忧郁 晨曦下的少女听着吉他旋律 在许愿池边巴洛克式的叹息 ——许愿池的希腊少女 完整代码见&#xff1a;SnowLegend-star/6.s081 at syscall (github.com) System call tracing (moderate) 这个实验要求我们跟踪系统调用。 感觉实…

软件设计师,下午题 ——试题六

模型图 简单工厂模式 工厂方法模式抽象工厂模式生成器模式原型模式适配器模式桥接模式组合模式装饰&#xff08;器&#xff09;模式亨元模式命令模式观察者模式状态模式策略模式访问者模式中介者模式 简单工厂模式 工厂方法模式 抽象工厂模式 生成器模式 原型模式 适配器模式 桥…

nodemcu32s 和 mini D1 组局域网并用 webSocket 通信

实现思路 使用 mini D1 来搭建一个 webSocket 服务&#xff0c;然后使用 nodemcu32 连接&#xff0c;然后就可以进行通信了。 服务端代码&#xff08;mini D1&#xff09; 在代码中主要是需要控制好 loop 函数中的延时&#xff0c;也就是最后一行代码 delay&#xff0c;如果…

大模型时代的具身智能系列专题(六)

UCSD 王小龙组 王小龙是UCSD电子与计算机工程系的助理教授。他曾在加州大学伯克利分校与Alexei Efros和Trevor Darrell一起担任博士后研究员&#xff0c;在CMU RI获得了机器人学博士学位&#xff0c;师从Abhinav Gupta。他的研究重点是通过视频和物理机器人交互数据来学习3D和…

【AIGC】大型语言模型在人工智能规划领域模型生成中的探索

大型语言模型在人工智能规划领域模型生成中的新应用 一、引言二、LLM在规划领域模型生成中的潜力三、实证分析&#xff1a;LLM在规划领域模型生成中的表现四、代码实例&#xff1a;LLM在规划领域模型生成中的应用五、结论与展望 一、引言 随着人工智能技术的迅猛发展&#xff0…

yolov5-ros模型结合zed2相机部署在 Ubuntu系统

前言 本篇文章主要讲解yolov5-ros模型结合zed2相机进行实时检测&#xff0c;经改进实现了红绿灯检测&#xff0c;并输出检测类别与置信度&#xff01; 目录 一、环境配置二、zed2驱动安装三、yolov5-ros功能包配置四、运行官方权重文件四、运行自己权重文件 一、环境配置 1、…

分享6个打开就能让人眼前一亮的网站,每次浏览都像发现新大陆~

1、ZLibrary zh.zlibrary-be.se/ ZLibrary是一个广受欢迎的在线图书馆&#xff0c;它提供了一个庞大的电子书和文章资源库&#xff0c;数量超过千万。这个平台覆盖了国内外众多领域的电子书资源&#xff0c;几乎可以满足用户98%以上的搜索需求&#xff0c;无论是学术研究、文…

Netty中半包粘包的产生与处理:短连接、固定长度、固定分隔符、预设长度;redis、http协议举例;网络数据的发送和接收过程

目录 粘包、半包 相关概念 网络数据发送和接收过程 Netty半包粘包解决方案 ByteBuf获取和默认大小 短链接 固定长度 固定分隔符 预设长度 常见协议代码举例 redis协议 http协议 参考链接 粘包、半包 相关概念 程序处理过程中我们会通过缓冲区接收数据&#xff0c…

input输入框的一些复习

<template><div><div style"text-align: center;margin: 10px 0;"><span style"font-size: 15px;font-weight: bold;">input输入框的基本应用</span></div><el-descriptions :column"3" size"defau…

Golang dlv远程debug

1. 前期准备 1.1. dlv安装 1.1.1. go install安装 go install github.com/go-delve/delve/cmd/dlvlatest1.1.2. 手动安装 下载 linuxx86 架构 二进制文件 &#x1f4ce;dlv.linux.x86.zip zip文件&#xff0c;下载后&#xff08; 如果没权限&#xff0c;记得 执行chmod ax…

linux下使用cmake-gui编译WXQT

一.编译环境 操作系统&#xff1a;Ubuntu 22.04.3 LTS wxWidgets源码&#xff1a;wxWidgets-3.1.5 编译工具&#xff1a;CMake-gui qt版本&#xff1a;5.13.2 二.编译步骤 1.将源码解压。 2.打开CMake-gui&#xff0c;并设置好源码目录和构建目录 3.点击configure 会弹出…

【c++入门】this指针

this指针引出&#xff1a; 我们知道一个类可以有多个实例化对象&#xff0c;但是这多个实例化对象所调用的成员函数是在公共代码区&#xff1b; 我们先来定义一个Date类&#xff1a; class Date { public:void init(int year, int month, int day){_year year;_month month;…

excel怎么对非数字求和汇总?

如&#xff1a;学生小王的成绩为&#xff1a;A&#xff0c;A&#xff0c;A&#xff0c;A&#xff0c;B&#xff0c;B-……想得到的成绩汇总求和为&#xff1a;2A,2A,1B,1B- 如果在低版本里&#xff0c;用公式计算可能相当复杂&#xff0c;但是有了TEXTJOIN函数和UNIQUE函数&…

一、实现一个简单的 Google Chrome 扩展程序

目录 &#x1f9ed; 效果展示 # 图示效果 a. 拓展程序列表图示效果&#xff1a; b. 当前选项卡页面右键效果&#xff1a; c. 拓展程序消息提示效果&#xff1a; &#x1f4c7; 项目目录结构 # 说明 # 结构 # 文件一览 ✍ 核心代码 # manifest.json # background.j…

数据结构算法 数组的实现与练习(C语言实现,Java实现)

文章目录 数据结构数组(顺序表)特点使用Java实现更高级的数组C语言实现总结优点缺点 例题[26. 删除有序数组中的重复项](https://leetcode.cn/problems/remove-duplicates-from-sorted-array/)[1. 两数之和](https://leetcode.cn/problems/two-sum/)[27. 移除元素](https://lee…

阿里云部署nodejs

目录 1、安装node.js 1-1 进入opt/software 1-2 下载node.js安装包 1-3 解压 2 配置环境变量 2-1 vim中配置环境变量 2-2 命令行中保存环境变量 2-3 检查安装版本 2-3 更换镜像 3、上传node.js项目 1-1 启动项目 1-2 配置对应的安全组 ​编辑 4、pm2启动多个node项…