Files
gzth/preview_easy/static/src/js/qweb.js
T
2025-06-10 20:24:58 +08:00

214 lines
7.5 KiB
JavaScript

/** @odoo-module **/
import { _t } from "@web/core/l10n/translation";
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
import {standardFieldProps} from "@web/views/fields/standard_field_props";
import { FileInput } from "@web/core/file_input/file_input";
import { useX2ManyCrud, useSelectCreate, useOpenMany2XRecord } from "@web/views/fields/relational_utils";
import { Component } from "@odoo/owl";
import { TagsList } from "@web/core/tags_list/tags_list";
import { Dialog } from "@web/core/dialog/dialog";
import {
Many2XAutocomplete,
useActiveActions,
} from "@web/views/fields/relational_utils";
import { Many2ManyTagsField, many2ManyTagsField } from "@web/views/fields/many2many_tags/many2many_tags_field";
export class Many2ManyBinaryField extends Many2ManyTagsField {
static template = "web.Many2ManyBinaryField";
static components = {
FileInput,
TagsList,
Many2XAutocomplete,
};
static props = {
...standardFieldProps,
acceptedFileExtensions: { type: String, optional: true },
className: { type: String, optional: true },
canCreate: { type: Boolean, optional: true },
canQuickCreate: { type: Boolean, optional: true },
canCreateEdit: { type: Boolean, optional: true },
colorField: { type: String, optional: true },
createDomain: { type: [Array, Boolean], optional: true },
domain: { type: [Array, Function], optional: true },
context: { type: Object, optional: true },
placeholder: { type: String, optional: true },
nameCreateField: { type: String, optional: true },
string: { type: String, optional: true },
noSearchMore: { type: Boolean, optional: true },
showSelectBtn: { type: Boolean, optional: true },
previewType: { type: String, optional: true },
previewString: { type: String, optional: true },
};
setup() {
super.setup()
this.orm = useService("orm");
this.notification = useService("notification");
this.operations = useX2ManyCrud(() => this.props.record.data[this.props.name], true);
const isToMany = true;
this.canCreate = false;
this.quickCreate = false;
this.openMany2X = useOpenMany2XRecord({
resModel: this.relation,
activeActions: this.activeActions,
isToMany: isToMany,
onRecordSaved: (record) => {
return this.update([{ ...record.data, id: record.resId }]);
},
onRecordDiscarded: () => {
if (!isToMany) {
this.update(false);
}
},
fieldString: this.string,
onClose: () => {
const autoCompleteInput = this.autoCompleteContainer?.el.querySelector("input");
if (!autoCompleteInput) {
return;
}
// There are two cases:
// 1. Value is the same as the input: it means the autocomplete has re-rendered with the right value
// This is in case we saved the record, triggering all the interface to update.
// 2. Value is different from the input: it means the input has a manually entered value and nothing
// happened, that is, we discarded the changes
if (this.props.value !== autoCompleteInput.value) {
autoCompleteInput.value = "";
}
autoCompleteInput.focus();
},
});
this.selectCreate = useSelectCreate({
resModel: this.relation,
activeActions: this.activeActions,
onSelected: (resId) => {
const resIds = Array.isArray(resId) ? resId : [resId];
const values = resIds.map((id) => ({ id }));
return this.update(values);
},
onCreateEdit: ({ context }) => this.openMany2X({ context }),
onUnselect: isToMany ? undefined : () => this.update(),
});
}
get uploadText() {
return this.props.record.fields[this.props.name].string;
}
get files() {
return this.props.record.data[this.props.name].records.map((record) => {
return {
...record.data,
id: record.resId,
};
});
}
getUrl(id) {
return "/web/content/" + id + "?download=true";
}
preview_Obj(id, name) {
var winStr = 'height=600,width=1000,top=' + (screen.height - 600) / 2 + ',left=' + (screen.width - 1000) / 2 + ',toolbar=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=no,location=no';
var newWin = window.open('/web/content/preview/'+ id , name, winStr);
newWin.focus();
}
getExtension(file) {
return file.name.replace(/^.*\./, "");
}
async onFileUploaded(files) {
for (const file of files) {
if (file.error) {
return this.notification.add(file.error, {
title: _t("Uploading error"),
type: "danger",
});
}
await this.operations.saveRecord([file.id]);
}
}
async onFileRemove(deleteId) {
const record = this.props.record.data[this.props.name].records.find(
(record) => record.resId === deleteId
);
this.operations.removeRecord(record);
}
async onExternalBtnClick() {
const domain = this.getDomain();
let dynamicFilters = [];
const nameGets = await this.orm.call(this.relation, "name_search", [], {
name: '',
args: domain,
operator: "ilike",
limit: this.props.searchMoreLimit,
context: this.props.context,
});
dynamicFilters = [
{
description: _t("Quick search"),
domain: [["id", "in", nameGets.map((nameGet) => nameGet[0])]],
},
];
const title = _t("Search");
this.selectCreate({
domain,
context: this.props.context,
filters: dynamicFilters,
title,
});
}
get documentsInstalled() {
try {
const result = $.ajax({
url: "/web/document_module/installed",
type: "POST",
async: false, // 设置为 false 表示同步请求
contentType: "application/json",
data: "{}"
});
return result?.responseJSON?.result?.installed;
} catch (error) {
console.error('Error fetching document installed status:', error);
return false; // 或者其他默认值
}
}
}
export const many2ManyBinaryField = {
component: Many2ManyBinaryField,
supportedOptions: [
{
label: _t("Accepted file extensions"),
name: "accepted_file_extensions",
type: "string",
},
],
supportedTypes: ["many2many"],
isEmpty: () => false,
relatedFields: [
{ name: "name", type: "char" },
{ name: "mimetype", type: "char" },
],
extractProps: ({ attrs, options }) => ({
acceptedFileExtensions: options.accepted_file_extensions,
showSelectBtn: options.show_select_btn === undefined ? true : options.show_select_btn,
previewType: options.preview_type,
previewString: options.preview_string,
className: attrs.class,
}),
};
registry.category("fields").add("preview_many2many", many2ManyBinaryField);