Odoo 15 – 基于hr_org_chart创建小部件 – SCSS不显示res.partner

huangapple go评论115阅读模式
英文:

Odoo 15 - Create widget based on hr_org_chart - SCSS don't visible od res.partner

问题

你的问题可能是由于SCSS文件的路径或命名问题导致的。请确保你的Odoo模块正确配置了SCSS文件的路径,以便在res.partner模型中加载它们。

首先,确保company_chart模块已经正确安装和启用。

然后,检查以下几个地方是否存在问题:

  1. SCSS文件路径:确保variables.scsscompany_chart.scss文件位于正确的路径下,也就是在company_chart模块的static/src/scss/目录中。

  2. SCSS文件命名:确保文件名的大小写和拼写与模块中的引用保持一致。比如,variables.scss应该与"company_chart/static/src/scss/variables.scss"中的路径一致。

  3. Manifest文件:在你的__manifest__.py文件中,确保'web.assets_backend'部分包含了正确的SCSS文件路径:

    1. 'web.assets_backend': [
    2. 'company_chart/static/src/scss/company_chart.scss',
    3. # ...
    4. ],

    同样,确保'web._assets_primary_variables'包含正确的variables.scss路径:

    1. 'web._assets_primary_variables': [
    2. 'company_chart/static/src/scss/variables.scss',
    3. # ...
    4. ],
  4. 重新加载模块:如果你已经更正了文件路径和命名,并且company_chart模块已启用,请确保在Odoo中重新加载该模块,以确保新的SCSS文件能够生效。

如果仍然存在问题,可能需要检查Odoo日志以查看是否有与SCSS文件加载相关的错误消息。如果日志中没有错误消息,那么问题可能涉及到其他方面,需要更详细的调试和分析。

最后,确保在浏览器中清除缓存,以确保新的SCSS文件能够加载并应用到res.partner模型中的小部件中。

英文:

I try to create widget to show company structure in odoo so i base on hr_org_chart (https://github.com/odoo/odoo/tree/15.0/addons/hr_org_chart), because it work as i want, with one thing show users but i want to see companies.

It start working me but on res.parner my widget don't use scss:

Odoo 15 – 基于hr_org_chart创建小部件 – SCSS不显示res.partner

when i use hr_org_widget in that same model (res.partner) i also don't have scss:
Odoo 15 – 基于hr_org_chart创建小部件 – SCSS不显示res.partner

in hr.employee show properly:

Odoo 15 – 基于hr_org_chart创建小部件 – SCSS不显示res.partner

My __manifest__.py:

  1. "depends": [
  2. "partner_multi_relation",
  3. "partner_multi_relation_customization",
  4. ],
  5. "auto_install": True,
  6. 'data': [
  7. "views/res_partner_views.xml",
  8. ],
  9. "assets": {
  10. "web._assets_primary_variables": [
  11. "company_chart/static/src/scss/variables.scss",
  12. ],
  13. "web.assets_backend": [
  14. "company_chart/static/src/scss/company_chart.scss",
  15. "company_chart/static/src/js/company_chart.js",
  16. ],
  17. "web.assets_qweb": [
  18. "company_chart/static/src/xml/**/*",
  19. ],
  20. },
  21. "license": "LGPL-3",
  22. }

variables.scss:

  1. $o-hr-org-chart-bg: white;
  2. $o-hr-org-chart-border-color: $o-brand-secondary;
  3. $o-hr-org-chart-entry-v-gap: 6px;
  4. $o-hr-org-chart-entry-pic-size: 46px;
  5. $o-hr-org-chart-entry-line-w: 1px;
  6. $o-hr-org-chart-entry-border-color: darken($o-hr-org-chart-bg, 25%);
  7. // MIXINS
  8. @mixin o-hr-org-chart-line {
  9. content: "";
  10. background-color: $o-hr-org-chart-bg;
  11. border: 0px solid $o-hr-org-chart-entry-border-color;
  12. }

company_chart.scss:

  1. // MOBILE LAYOUT CUSTOMIZATIONS
  2. @include media-breakpoint-down(sm) {
  3. #o_employee_right {
  4. .o_org_chart_title {
  5. font-size: 20px;
  6. padding: 5px 0;
  7. border-bottom: 1px solid $o-hr-org-chart-border-color;
  8. }
  9. }
  10. }
  11. // SMALL DESKTOP LAYOUT
  12. @include media-breakpoint-up(md) {
  13. #o_work_employee_container {
  14. display: flex;
  15. width: 100%;
  16. }
  17. #o_work_employee_main {
  18. flex: 1 1 60%;
  19. }
  20. #o_employee_right {
  21. flex: 0 1 35%;
  22. margin-left: 2%;
  23. padding-left: 2%;
  24. border-left: 1px solid $o-hr-org-chart-border-color;
  25. .o_org_chart_title {
  26. color: gray("600");
  27. }
  28. }
  29. }
  30. // MEDIUM DESKTOP LAYOUT
  31. @include media-breakpoint-up(lg) {
  32. #o_employee_right {
  33. flex: 0 1 33%;
  34. }
  35. }
  36. // LARGE DESKTOP LAYOUT
  37. @include media-breakpoint-up(xl) {
  38. #o_employee_right {
  39. flex: 0 1 30%;
  40. }
  41. }
  42. #o_employee_right {
  43. $tmp-gap-base: $o-hr-org-chart-entry-pic-size * 0.7;
  44. // ORGANIGRAM LINES
  45. .o_field_widget,
  46. .o_org_chart_group_up,
  47. .o_org_chart_group_down {
  48. position: relative;
  49. width: 100%;
  50. }
  51. .o_org_chart_group_up {
  52. &:before {
  53. @include o-hr-org-chart-line;
  54. border-left-width: $o-hr-org-chart-entry-line-w;
  55. height: calc(100% + #{$o-hr-org-chart-entry-pic-size * 0.5});
  56. @include o-position-absolute(
  57. $top: $o-hr-org-chart-entry-pic-size * 0.1 + 5px,
  58. $left: $o-hr-org-chart-entry-pic-size * 0.5 -
  59. $o-hr-org-chart-entry-line-w * 0.5
  60. );
  61. }
  62. .o_org_chart_entry:last-of-type {
  63. &:before {
  64. @include o-hr-org-chart-line;
  65. border-width: 0 0 $o-hr-org-chart-entry-line-w
  66. $o-hr-org-chart-entry-line-w;
  67. @include size(
  68. ($o-hr-org-chart-entry-pic-size * 0.5) -
  69. ($o-hr-org-chart-entry-v-gap * 2),
  70. $o-hr-org-chart-entry-pic-size * 0.5 + $o-hr-org-chart-entry-v-gap * 2
  71. );
  72. @include o-position-absolute(
  73. $left: $o-hr-org-chart-entry-pic-size * 0.5 -
  74. $o-hr-org-chart-entry-line-w * 0.5,
  75. $top: 100%
  76. );
  77. }
  78. }
  79. }
  80. .o_org_chart_group_up + .o_org_chart_entry_self {
  81. margin-left: $tmp-gap-base;
  82. & + .o_org_chart_group_down {
  83. padding-left: $tmp-gap-base * 2;
  84. &:before {
  85. margin-left: $tmp-gap-base;
  86. }
  87. }
  88. }
  89. .o_org_chart_group_down {
  90. padding-left: $tmp-gap-base;
  91. &:before {
  92. @include o-hr-org-chart-line;
  93. border-left-width: $o-hr-org-chart-entry-line-w;
  94. height: 100%;
  95. @include o-position-absolute(
  96. $top: $o-hr-org-chart-entry-v-gap * -1,
  97. $left: $tmp-gap-base * 0.5 + $o-hr-org-chart-entry-pic-size * 0.1 +
  98. $o-hr-org-chart-entry-line-w * 0.5
  99. );
  100. }
  101. .o_org_chart_entry {
  102. &:before {
  103. @include o-hr-org-chart-line;
  104. border-top-width: $o-hr-org-chart-entry-line-w;
  105. @include size($tmp-gap-base, 0);
  106. @include o-position-absolute(
  107. $left: $tmp-gap-base * -0.5 + $o-hr-org-chart-entry-pic-size * 0.1 +
  108. $o-hr-org-chart-entry-line-w * 0.5,
  109. $top: $o-hr-org-chart-entry-pic-size * 0.5
  110. );
  111. }
  112. &:last-of-type {
  113. &:before {
  114. height: 50%;
  115. }
  116. }
  117. &.o_org_chart_more {
  118. margin-top: $o-hr-org-chart-entry-v-gap;
  119. &:before {
  120. top: 15px;
  121. }
  122. }
  123. }
  124. }
  125. // ORGANIGRAM DESIGN
  126. .o_org_chart_entry {
  127. margin-bottom: $o-hr-org-chart-entry-v-gap;
  128. overflow: visible;
  129. margin-top: 0;
  130. &,
  131. .o_media_left,
  132. .media-body {
  133. position: relative;
  134. }
  135. .o_media_left {
  136. padding-right: 10px;
  137. }
  138. .media-body {
  139. vertical-align: middle;
  140. .badge {
  141. float: right;
  142. cursor: pointer;
  143. margin-right: 5px;
  144. color: gray("600");
  145. background: $o-hr-org-chart-bg;
  146. border: 1px solid gray("600");
  147. &:hover {
  148. color: $o-brand-primary;
  149. border-color: $o-brand-primary;
  150. }
  151. &:focus {
  152. outline: none;
  153. }
  154. }
  155. strong {
  156. display: block;
  157. line-height: 1.2;
  158. font-size: 11px;
  159. color: lighten(gray("600"), 15%);
  160. }
  161. }
  162. .o_media_object {
  163. display: block;
  164. width: $o-hr-org-chart-entry-pic-size * 0.8;
  165. height: $o-hr-org-chart-entry-pic-size * 0.8;
  166. margin: $o-hr-org-chart-entry-pic-size * 0.1;
  167. box-shadow: 0 0 0 $o-hr-org-chart-entry-line-w
  168. darken($o-hr-org-chart-bg, 20%);
  169. background-size: cover;
  170. background-position: center center;
  171. background-color: $o-view-background-color;
  172. &.card {
  173. height: 20px;
  174. box-shadow: none;
  175. border-color: transparent;
  176. padding: 0;
  177. position: relative;
  178. color: $body-color;
  179. .o_org_chart_show_more {
  180. line-height: 13px;
  181. }
  182. &:hover {
  183. border-color: $o-hr-org-chart-entry-border-color;
  184. color: $o-brand-primary;
  185. }
  186. }
  187. }
  188. &.o_org_chart_entry_manager,
  189. &.o_org_chart_entry_sub {
  190. .o_media_left {
  191. padding-right: 0;
  192. }
  193. .media-body > a {
  194. padding-left: 10px;
  195. max-width: 100%;
  196. display: block;
  197. .o_media_heading {
  198. color: lighten(gray("600"), 5%);
  199. font-size: 13px;
  200. }
  201. }
  202. &:hover {
  203. .o_media_object {
  204. box-shadow: 0 0 0 $o-hr-org-chart-entry-line-w * 2
  205. rgba($o-brand-primary, 0.6);
  206. }
  207. .media-body > a {
  208. .o_media_heading {
  209. color: $o-brand-primary;
  210. }
  211. strong {
  212. color: lighten(gray("600"), 5%);
  213. }
  214. }
  215. }
  216. }
  217. &.o_org_chart_entry_self {
  218. &:not(:first-child) {
  219. margin-top: $o-hr-org-chart-entry-v-gap * 1.5;
  220. }
  221. strong {
  222. color: $text-muted;
  223. }
  224. .o_media_object {
  225. width: $o-hr-org-chart-entry-pic-size;
  226. height: $o-hr-org-chart-entry-pic-size;
  227. margin: 0;
  228. border: $o-hr-org-chart-entry-line-w * 2 solid $o-brand-primary;
  229. box-shadow: inset 0 0 0 $o-hr-org-chart-entry-line-w * 2 white;
  230. }
  231. .media-body {
  232. opacity: 1;
  233. }
  234. }
  235. }
  236. }
  237. // POP OVER
  238. .o_org_chart_popup.popover {
  239. max-width: 400px;
  240. margin-right: 5px;
  241. .popover-header {
  242. height: 47px;
  243. line-height: 33px;
  244. padding-right: 50px;
  245. > a {
  246. @include o-position-absolute($right: 14px);
  247. }
  248. span {
  249. @include size(30px, 30px);
  250. margin-right: 10px;
  251. border-radius: 100%;
  252. background-position: center;
  253. background-size: cover;
  254. float: left;
  255. box-shadow: 0 1px 1px;
  256. }
  257. }
  258. .table {
  259. margin-bottom: 0;
  260. }
  261. }
  262. // Right to Left specific style to flip the popover arrow
  263. .o_rtl {
  264. .o_org_chart_popup.popover .arrow {
  265. left: 100%;
  266. -webkit-transform: matrix(-1, 0, 0, 1, 0, 0);
  267. -moz-transform: matrix(-1, 0, 0, 1, 0, 0);
  268. -o-transform: matrix(-1, 0, 0, 1, 0, 0);
  269. transform: matrix(-1, 0, 0, 1, 0, 0);
  270. }
  271. }

company_chart.js:

  1. odoo.define("web.CompanyChart", function (require) {
  2. "use strict";
  3. var AbstractField = require("web.AbstractField");
  4. var concurrency = require("web.concurrency");
  5. var core = require("web.core");
  6. var field_registry = require("web.field_registry");
  7. var session = require("web.session");
  8. var QWeb = core.qweb;
  9. var _t = core._t;
  10. var CompanyChart = AbstractField.extend({
  11. events: {
  12. "click .o_employee_redirect": "_onEmployeeRedirect",
  13. "click .o_employee_sub_redirect": "_onEmployeeSubRedirect",
  14. "click .o_employee_more_managers": "_onEmployeeMoreManager",
  15. },
  16. /**
  17. * @constructor
  18. * @override
  19. */
  20. init: function (parent, options) {
  21. this._super.apply(this, arguments);
  22. this.dm = new concurrency.DropMisordered();
  23. this.employee = null;
  24. },
  25. //--------------------------------------------------------------------------
  26. // Private
  27. //--------------------------------------------------------------------------
  28. /**
  29. * Get the chart data through a rpc call.
  30. *
  31. * @private
  32. * @param {integer} employee_id
  33. * @returns {Promise}
  34. */
  35. _getOrgData: function () {
  36. var self = this;
  37. return this.dm
  38. .add(
  39. this._rpc({
  40. route: "/company/get_org_chart",
  41. params: {
  42. employee_id: this.employee,
  43. context: session.user_context,
  44. },
  45. })
  46. )
  47. .then(function (data) {
  48. return data;
  49. });
  50. },
  51. /**
  52. * Get subordonates of an employee through a rpc call.
  53. *
  54. * @private
  55. * @param {integer} employee_id
  56. * @returns {Promise}
  57. */
  58. _getSubordinatesData: function (employee_id, type) {
  59. return this.dm.add(
  60. this._rpc({
  61. route: "/company/get_subordinates",
  62. params: {
  63. employee_id: employee_id,
  64. subordinates_type: type,
  65. context: session.user_context,
  66. },
  67. })
  68. );
  69. },
  70. /**
  71. * @override
  72. * @private
  73. */
  74. _render: function () {
  75. if (!this.recordData.id) {
  76. return this.$el.html(
  77. QWeb.render("company_chart", {
  78. managers: [],
  79. children: [],
  80. })
  81. );
  82. } else if (!this.employee) {
  83. // the widget is either dispayed in the context of a res.partner form or a res.users form
  84. this.employee =
  85. this.recordData.employee_ids !== undefined
  86. ? this.recordData.employee_ids.res_ids[0]
  87. : this.recordData.id;
  88. }
  89. var self = this;
  90. return this._getOrgData().then(function (orgData) {
  91. if (_.isEmpty(orgData)) {
  92. orgData = {
  93. managers: [],
  94. children: [],
  95. };
  96. }
  97. orgData.view_employee_id = self.recordData.id;
  98. console.log(orgData)
  99. self.$el.html(QWeb.render("company_chart", orgData));
  100. self.$('[data-toggle="popover"]').each(function () {
  101. $(this).popover({
  102. html: true,
  103. title: function () {
  104. var $title = $(
  105. QWeb.render("company_chart_emp_popover_title", {
  106. employee: {
  107. name: $(this).data("emp-name"),
  108. id: $(this).data("emp-id"),
  109. },
  110. })
  111. );
  112. $title.on(
  113. "click",
  114. ".o_employee_redirect",
  115. _.bind(self._onEmployeeRedirect, self)
  116. );
  117. return $title;
  118. },
  119. container: this,
  120. placement: "left",
  121. trigger: "focus",
  122. content: function () {
  123. var $content = $(
  124. QWeb.render("company_chart_emp_popover_content", {
  125. employee: {
  126. id: $(this).data("emp-id"),
  127. name: $(this).data("emp-name"),
  128. direct_sub_count: parseInt(
  129. $(this).data("emp-dir-subs"),
  130. 10
  131. ),
  132. indirect_sub_count: parseInt(
  133. $(this).data("emp-ind-subs"),
  134. 10
  135. ),
  136. },
  137. })
  138. );
  139. $content.on(
  140. "click",
  141. ".o_employee_sub_redirect",
  142. _.bind(self._onEmployeeSubRedirect, self)
  143. );
  144. return $content;
  145. },
  146. template: QWeb.render("company_chart_emp_popover", {}),
  147. });
  148. });
  149. });
  150. },
  151. //--------------------------------------------------------------------------
  152. // Handlers
  153. //--------------------------------------------------------------------------
  154. _onEmployeeMoreManager: function (event) {
  155. event.preventDefault();
  156. this.employee = parseInt($(event.currentTarget).data("employee-id"), 10);
  157. this._render();
  158. },
  159. /**
  160. * Redirect to the employee form view.
  161. *
  162. * @private
  163. * @param {MouseEvent} event
  164. * @returns {Promise} action loaded
  165. */
  166. _onEmployeeRedirect: function (event) {
  167. var self = this;
  168. event.preventDefault();
  169. var employee_id = parseInt(
  170. $(event.currentTarget).data("employee-id"),
  171. 10
  172. );
  173. return this._rpc({
  174. model: "res.partner",
  175. method: "get_formview_action",
  176. args: [employee_id],
  177. }).then(function (action) {
  178. return self.do_action(action);
  179. });
  180. },
  181. /**
  182. * Redirect to the sub employee form view.
  183. *
  184. * @private
  185. * @param {MouseEvent} event
  186. * @returns {Promise} action loaded
  187. */
  188. _onEmployeeSubRedirect: function (event) {
  189. event.preventDefault();
  190. var employee_id = parseInt(
  191. $(event.currentTarget).data("employee-id"),
  192. 10
  193. );
  194. var employee_name = $(event.currentTarget).data("employee-name");
  195. var type = $(event.currentTarget).data("type") || "direct";
  196. var self = this;
  197. if (employee_id) {
  198. this._getSubordinatesData(employee_id, type).then(function (data) {
  199. var domain = [["id", "in", data]];
  200. return self
  201. ._rpc({
  202. model: "res.partner",
  203. method: "get_formview_action",
  204. args: [employee_id],
  205. })
  206. .then(function (action) {
  207. action = _.extend(action, {
  208. name: _t("Team"),
  209. view_mode: "kanban,list,form",
  210. views: [
  211. [false, "kanban"],
  212. [false, "list"],
  213. [false, "form"],
  214. ],
  215. domain: domain,
  216. context: {
  217. default_parent_id: employee_id,
  218. },
  219. });
  220. delete action.res_id;
  221. return self.do_action(action);
  222. });
  223. });
  224. }
  225. },
  226. });
  227. field_registry.add("company_chart", CompanyChart);
  228. return CompanyChart;
  229. });

What i do wrong that scss from assets don't load at my widget in res.partner ?

答案1

得分: 0

我忘记在XML中添加了以下内容:<div id="o_employee_right">在表单视图中:

  1. <div id="o_employee_right">
  2. <h4 class="o_org_chart_title mb16 mt0">组织图表</h4>
  3. <field name="partner_relations" widget="company_chart"/>
  4. </div>

现在它可以工作了!

英文:

Ok i forgot to add in xml : <div id="o_employee_right"> in form view:

  1. <div id="o_employee_right">
  2. <h4 class="o_org_chart_title mb16 mt0">Organization Chart</h4>
  3. <field name="partner_relations" widget="company_chart"/>
  4. </div>

Now it works!

huangapple
  • 本文由 发表于 2023年7月13日 18:05:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76678192.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定