@删除单个热映数据
准备删除接口
api/movieApi.js
/* 删除影片 */
// async函数的返回值是Promise对象
export async function deletePlaying(id) {
const {
msg,
data: { deletedCount },
} = await doDelete(`/film/${id}`);
// return相当于Promise在履约
return { msg, deletedCount };
}
触发删除操作
views/film/Detail.vue
<!-- 从作用域插槽数据中解构出当前行id -->
<template #default="{ row: { _id } }">
<!-- 点击Edit按钮 携带id跳转详情页 -->
<el-button
@click="$router.push(`/film/${_id}`)"
type="primary"
:icon="Edit"
circle
size="small"
/>
<!-- 触发单个影片删除 -->
<el-button
@click="deleteItem(_id)"
type="danger"
:icon="Delete"
circle
size="small"
/>
</template>
调用接口执行删除
views/film/Detail.vue
import { deletePlaying } from "@api/movieApi";
// 调用服务端API执行删除
const { msg, deletedCount } = await deletePlaying(deleteId.value);
console.log("msg=", msg);
删除后反馈+刷新页面
import { ElMessage } from "element-plus";
// 调用服务端API执行删除
const { msg, deletedCount } = await deletePlaying(deleteId.value);
console.log("msg=", msg);
/* 使用Message提示信息 */
ElMessage({
message: msg,
type: deletedCount ? "success" : "error",
});
if (deletedCount) {
// 暴力重绘整个页面(性能低下,体验垃圾,横批——你个渣渣)
// window.location.reload();
/* 修改响应式数据,让数据去驱动视图做【差量渲染】 */
// 找出要删除的那条数据 arr.find(item=>item._id===xxid)
tableData.value.find((item, index) => {
if (item._id === deleteId.value) {
/* 等find正常返回后 再执行真正的删除动作 */
setTimeout(() => {
tableData.value.splice(index, 1);
});
return item;
}
});
}
@删除前使用弹窗确认
界面上准备好弹窗,并先行隐藏
<!-- 默认隐藏的对话框 -->
<el-dialog
v-model="dialogVisible"
title="操作确认"
width="30%"
:before-close="handleClose"
>
<span>{{ dialogMode.msg }}</span>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="dialogMode.callback">
确认
</el-button>
</span>
</template>
</el-dialog>
弹窗的关联数据
- 使用一个ref控制弹窗的显隐
// 对话框显隐控制
const dialogVisible = ref(false);
- 使用一组弹窗模式控制弹窗的提示文字+确认回调
/* 对话框模式 */
const dialogModes = {
// 单个删除模式
deleteItem: {
msg: "确认删除影片吗?",
callback: doDeleteItem,
},
// 批量删除模式
patchDelete: {
msg: "确认执行批量删除吗?",
callback: doPatchDelete,
},
};
// 默认使用单个删除模式
let dialogMode = ref(dialogModes.deleteItem);
完整的删除流程
- 用户点击删除
- 暂存要删除的影片ID
- 设置好弹窗模式:内置提示文字 + 确认时的回调函数
- 用户点击确认时调出先前暂存好的影片ID并执行删除
- 提示删除结果 + 刷新页面
开始删除
/* 删除单个 */
const deleteId = ref(0);
const deleteItem = (id) => {
// 先将要删除的id暂存起来
deleteId.value = id;
// 确认删除对话框显示粗来
dialogMode.value = dialogModes.deleteItem;
dialogVisible.value = true;
};
用户点击确认
<!-- 用户点击确认时执行【当前模式对应的回调】 -->
<el-button type="primary" @click="dialogMode.callback">
确认
</el-button>
执行删除
/* 执行单个删除 */
const doDeleteItem = async () => {
// 先读入要删除的id
console.log("doDeleteItem", deleteId.value);
// 将对话框隐藏
dialogVisible.value = false;
// 调用服务端API执行删除
const { msg, deletedCount } = await deletePlaying(deleteId.value);
console.log("msg=", msg);
/* 使用Message提示信息 */
ElMessage({
message: msg,
type: deletedCount ? "success" : "error",
});
if (deletedCount) {
// 暴力重绘整个页面(性能低下,体验垃圾,横批——你个渣渣)
// window.location.reload();
/* 修改响应式数据,让数据去驱动视图做【差量渲染】 */
// 找出要删除的那条数据 arr.find(item=>item._id===xxid)
tableData.value.find((item, index) => {
if (item._id === deleteId.value) {
/* 等find正常返回后 再执行真正的删除动作 */
setTimeout(() => {
tableData.value.splice(index, 1);
});
return item;
}
});
}
};
@批量删除
获取用户批量选择的数组
<!--
//多选变化时的处理函数,载荷为勾选上的数据集
@selection-change="handleSelectionChange"
-->
<el-table
:data="computedData"
stripe
class="middle"
style="width: 100%"
:default-sort="{ prop: 'date', order: 'ascending' }"
@selection-change="handleSelectionChange"
>
...
</el-table>
/* 多选时此处能拿到选中的子数组 */
const selectedItems = ref([]);
const handleSelectionChange = (val) => {
console.log("handleSelectionChange", val);
selectedItems.value = val;
};
触发批量删除
<el-button class="opBtn" type="danger" @click="patchDelete">
<el-icon><Close /></el-icon> 删除
</el-button>
先弹窗确认
const patchDelete = () => {
// 确认删除对话框显示粗来
dialogMode.value = dialogModes.patchDelete;
dialogVisible.value = true;
};
用户确认执行批量删除
<!-- 用户点击确认时执行【当前模式对应的回调】 -->
<el-button type="primary" @click="dialogMode.callback">
确认
</el-button>
执行批量删除,反馈结果,刷新页面
/* 执行批量删除 */
const doPatchDelete = () => {
dialogVisible.value = false;
console.log("doPatchDelete");
Promise.all(
/* 将选中的【电影数组】映射为【执行删除的Promise数组】 */
selectedItems.value.map((film) => deletePlaying(film._id))
)
.then((results) => {
ElMessage({
message: "批量删除成功",
type: "success",
});
// 依然应该使用数据驱动视图 这里做了简单处理
window.location.reload()
})
.catch((err) => {
ElMessage({
message: err,
type: "error",
});
});
};
@更新模块数据
用户点击进入详情页
<!-- 右侧固定的操作按钮区 -->
<el-table-column fixed="right" label="操作" width="90">
<!-- 从作用域插槽数据中解构出当前行id -->
<template #default="{ row: { _id } }">
<!-- 点击Edit按钮 携带id跳转详情页 -->
<el-button
@click="$router.push(`/film/${_id}`)"
type="primary"
:icon="Edit"
circle
size="small"
/>
...
</template>
</el-table-column>
用户修改表单数据
所有的表单元素都使用了v-model双向数据绑定
,将数据实时同步给响应式对象form
// 表单数据存储器
const form = reactive({});
<!-- 详情表单 -->
<!-- form没有数据时不渲染form -->
<el-form v-if="form[`name`]" :model="form" label-width="120px">
<!-- 片名关联form.name -->
<el-form-item label="片名">
<el-input v-model="form.name" />
</el-form-item>
<!-- 片长关联form.runtime -->
<el-form-item label="片长">
<el-col :span="7">
<el-input v-model="form.runtime" type="number" />
</el-col>
</el-form-item>
<!-- 评分关联form.grade -->
<el-form-item label="评分">
<el-col :span="7">
<el-input v-model="form.grade" />
</el-col>
</el-form-item>
<!-- 影片类型关联form.filmType.name -->
<!-- filmType?.name 如果filmType不为null/undeined 就继续读取filmType?.name 否则直接返回null/undeined -->
<el-form-item label="影片类型">
<el-select
v-model="form.filmType.name"
placeholder="please select your zone"
>
<el-option label="2D" value="2D" />
<el-option label="3D" value="3D" />
<el-option label="4D" value="4D" />
</el-select>
</el-form-item>
<!-- 影片类型关联form.premiereAt -->
<el-form-item label="首映日期">
<el-col :span="8">
<el-date-picker
v-model="form.premiereAt"
type="date"
placeholder="Pick a date"
style="width: 100%"
/>
</el-col>
</el-form-item>
<!-- 在映关联form.isPresale -->
<el-form-item label="在映">
<el-switch v-model="form.isPresale" />
</el-form-item>
<!-- 影片类型关联form.category -->
<el-form-item label="影片类型">
<el-checkbox-group v-model="form.category">
<el-checkbox label="爱情" name="category" />
<el-checkbox label="动作" name="category" />
<el-checkbox label="科幻" name="category" />
<el-checkbox label="历史" name="category" />
<el-checkbox label="悬疑" name="category" />
<el-checkbox label="喜剧" name="category" />
<el-checkbox label="战争" name="category" />
<el-checkbox label="剧情" name="category" />
<el-checkbox label="犯罪" name="category" />
<el-checkbox label="纪录片" name="category" />
</el-checkbox-group>
</el-form-item>
<!-- 国家关联form.nation -->
<el-form-item label="国家">
<el-radio-group v-model="form.nation">
<el-radio label="中国大陆" />
<el-radio label="欧美" />
<el-radio label="日韩" />
<el-radio label="其它" />
</el-radio-group>
</el-form-item>
<!-- 演职人员关联form.actors -->
<!--
:on-preview="handlePreview"
:on-remove="handleRemove"
-->
<el-form-item label="使用照片墙">
<el-switch v-model="usePictureCard" />
{{ usePictureCard }}
</el-form-item>
<el-form-item label="演职人员">
<el-upload
v-model:file-list="form.actors"
class="upload-demo"
:list-type="usePictureCard ? 'picture-card' : 'picture'"
:auto-upload="true"
action="/api/file/upload"
:on-success="onActorUploadSuccess"
>
<el-icon><Plus /></el-icon>
</el-upload>
</el-form-item>
<!-- 海报关联form.poster -->
<el-form-item label="海报">
<el-upload
v-model:file-list="form.poster"
action="/api/file/upload"
list-type="picture-card"
:on-success="onPosterUploadSuccess"
>
<el-icon><Plus /></el-icon>
</el-upload>
</el-form-item>
<!-- 剧情摘要form.synopsis -->
<el-form-item label="剧情摘要">
<el-input v-model="form.synopsis" autosize type="textarea" />
</el-form-item>
<!-- 提交与重置 -->
<el-form-item>
<el-button class="opBtn" type="primary" @click="onSubmit"
>更新</el-button
>
<el-button class="opBtn">重置</el-button>
</el-form-item>
</el-form>
用户点击提交
- 拿到实时表单数据
const onSubmit = async () => {
console.log("submit!", form);
...
};
去格式化表单数据
- 对数据执行【去格式化】动作,使表单数据匹配回后台需要的格式
- 这个动作和加载到后台数据时将修改为表单所需的【格式化】动作是互逆操作
const onSubmit = async () => {
console.log("submit!", form);
/* 对数据做必要的【去格式化】的动作 */
const deformatedForm = Object.assign({}, form, {
// 影片类型: 数组转字符串
category: form.category.join("|"),
// 首映日期: Date转时间戳
premiereAt: form.premiereAt / 1000,
// 海报:[{name,url},...]数组转字符串
poster: form.poster.map((picture) => picture.url).join(""),
// 演员表: [{name,url}]=>[{name,role,avatarAddress}]
actors: form.actors.map(({ name, url }) => {
let [_name, _role] = name.split("-");
let avatarAddress = url;
return {
name: _name,
role: _role,
avatarAddress,
};
}),
});
// 删除deformatedForm中的id(修改id对于数据库来说是非法操作)
delete deformatedForm._id;
console.log("after deformat", deformatedForm);
/* 向服务端发起请求 */
...
};
执行数据更新
/* 向服务端发起请求 */
const { msg, modifiedCount } = await updatePlaying(form._id, deformatedForm);
/* 反馈用户更新信息结果 */
ElMessage({
message: msg,
type: modifiedCount ? "success" : "error",
});
// 暴力刷新查看更新结果
setTimeout(() => {
window.location.reload();
}, 500);
@图片上传
配置服务端上传接口
- 注意:ElementPlus的Upload组件
上传文件的input框的name字段
的值为file
,服务端接收的时候需要注意; action
即服务端上传接口,用户选择好文件后会自动向服务端上传,服务端配置好接收接口即可;- 由于跨域问题的存在,
action="/api/file/upload"
中的/api
代表本地路由,这个前缀在执行网络上传时会被覆盖为服务端的baseUrl
; on-success
为文件上传成功后的回调函数,稍后我们需要在表单数据中将图片的地址修改为服务端地址;
<!-- 演职人员关联form.actors -->
<el-form-item label="演职人员">
<el-upload
v-model:file-list="form.actors"
class="upload-demo"
:list-type="usePictureCard ? 'picture-card' : 'picture'"
:auto-upload="true"
action="/api/file/upload"
:on-success="onActorUploadSuccess"
>
<el-icon><Plus /></el-icon>
</el-upload>
</el-form-item>
<!-- 海报关联form.poster -->
<el-form-item label="海报">
<el-upload
v-model:file-list="form.poster"
action="/api/file/upload"
list-type="picture-card"
:on-success="onPosterUploadSuccess"
>
<el-icon><Plus /></el-icon>
</el-upload>
</el-form-item>
文件上传成功的数据加工
- 文件上传成功,其url只是一个临时url,我们需要修改其目标地址为服务器端文件的访问地址
/* 海报上传成功回调 */
const onPosterUploadSuccess = (response, uploadFile, uploadFiles) => {
console.log(
"response, uploadFile, uploadFiles=",
response,
uploadFile,
uploadFiles
);
// 将海报的信息中的poster字段做修改
form.poster = [
{
name: "",
url: `http://localhost:8173/upload/${uploadFile.name}`,
},
];
};
/* 演员照片上传成功回调 */
const onActorUploadSuccess = (response, uploadFile, uploadFiles) => {
console.log(
"response, uploadFile, uploadFiles=",
response,
uploadFile,
uploadFiles
);
// 新上传的文件位于actors列表的末尾,更正这个数据
let lastActor = form.actors[form.actors.length - 1];
let [name, role] = uploadFile.name.split("-");
console.log("name,role", name, role);
role = role.split(".")[0]; //去掉文件名后缀
/* 更新表单数据 */
Object.assign(lastActor, {
name: `${name}-${role}`,
url: `http://localhost:8173/upload/${uploadFile.name}`,
});
};
三连了没就走???
watch,follow,fork!!!
祝大家撸码愉快~
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
暂无评论内容